【问题标题】:Cast Laravel query results to class将 Laravel 查询结果转换为类
【发布时间】:2015-10-30 14:53:15
【问题描述】:

使用语法DB::table('foo') 创建查询时,它会创建一个泛型类(stdClass)。有没有办法将结果行转换为特定的类?

这里有一些示例代码应该解释我想要做什么:

$result = DB::table('foo')->get();
$converted = (Foo) $result; // Not going to work

我想将数组的所有 (stdClass) 对象转换为 Foo 类。

【问题讨论】:

    标签: php laravel laravel-5 eloquent


    【解决方案1】:

    是的,您可以将结果水合到您想要的课程中。我发现答案深埋在一堆半答案和令人困惑的问题中,这些问题构成了可怕的Laracasts.com forum。感谢您在这里而不是那里提出问题。

    获得结果后,使用您的模型类对它们进行水合:

    $result = DB::table('foo')->get();
    $converted = Foo::hydrate($result);
    

    编辑:也找到了一些documentation on the hydrate method,如果有帮助的话

    编辑 2: 我发现自己需要从 arraycollection 转换结果,具体取决于查询的结果。当一个集合被返回时,它被正确地水合了,但是当结果是一个数组时,它们只是stdClassI wrote a quick method 添加到我的主人 model 中,它采用数组或对象或分页对象的集合,并将其正确地转换为我想要的对象。

    【讨论】:

    • 但是如果你使用分页呢。例如。 $coll = DB::table('Foo')->orderBy('title')->paginate(30);然后水合作用失败......有什么想法吗? (对不起 - 我是 laravel 的新手)
    • @derRobert 不用担心——有时在 Laravel 中做一些看似正常或合乎逻辑的事情可能会非常辛苦。调用 pagination 返回一个包含结果的 AbstractPaginator 对象,因此需要在分页器中对其进行转换。我从上面的编辑 2 链接到我写的一个函数,就是这样做的。取上面的$coll,并使用我的函数进行转换:Foo::castResults($coll)
    【解决方案2】:

    从 laravel 5.4 开始

    Eloquent 模型没有 hydrate() 方法。

    这已移至 Eloquent Builder 类。

    https://laravel.com/api/5.4/search.html?search=hydrate

    Eloquent Builder 类需要一些东西才能手动完成这项工作

    1. 要初始化的查询构建器
    2. 保湿模型。

    这是我在一些 laravel 5.8 代码中的工作示例:

                $instance = new $class;
                $table = $instance->getTable();
    
                
                $eloquent_builder = new \Illuminate\Database\Eloquent\Builder(
                    // the Query Builder!
                    DB::connection($connection_name)
                    ->table($table)
                    ->select($columns)
                    ->orderBy($order_by, $order)
                );
                
    
                // Tell Eloquent what you're using to hydrate the query
                $eloquent_builder->setModel($instance);
    
    
                return $eloquent_builder->get();
    

    【讨论】:

      【解决方案3】:

      通常,您可以通过将 PDO 语句 fetch_style 设置为 PDO::FETCH_CLASS 来实现这一点,如下所示

      $statement->fetchAll(PDO::FETCH_CLASS, "App\User");

      如果您查看Illuminate\Database\Connection::select 方法,您会发现虽然您可以设置 fetch_style/fetchMode,但不能设置第二个参数。

      public function select($query, $bindings = array(), $useReadPdo = true)
      {
          return $this->run($query, $bindings, function($me, $query, $bindings) use ($useReadPdo)
          {
              if ($me->pretending()) return array();
      
              // For select statements, we'll simply execute the query and return an array
              // of the database result set. Each element in the array will be a single
              // row from the database table, and will either be an array or objects.
              $statement = $this->getPdoForSelect($useReadPdo)->prepare($query);
      
              $statement->execute($me->prepareBindings($bindings));
      
              return $statement->fetchAll($me->getFetchMode());
          });
      }
      

      例如,在调用 fetchAll 以调用 PDOStatement::setFetchMode 之前,您也无法访问该语句。

      您或许可以尝试扩展 Illuminate\Database\Connection,并通过在必要时扩展和替换来在其他与数据库相关的类中使用它,但维护起来似乎是一项艰巨的任务。

      另一种选择是使用 Eloquent,它会为您提供特定类型的类,但您会获得一些额外的水合模型对象的开销。

      class Foo extends Illuminate\Database\Eloquent\Model {
          protected $table = 'foo';
      }
      
      Foo::all()
      Foo::where('col', 1)->get()
      

      【讨论】:

        【解决方案4】:

        我发现此页面正在寻找将 Laravel 查询重新转换为特定模型的解决方案。在 Laravel 7/8 中,可以将查询构建器的结果转换为特定的 Laravel 模型,然后您可以将其与 eloquent 一起使用。

        您可以通过DB门面提供的table方法开始查询:

        $query = DB::table('bar');
        // You can add table joins, where statements, etc.
        

        然后使用以下方法将上述查询转换为 Foo 模型:

        $builder = \App\Foo::select('foo.*');
        $builder->setQuery($query);
        

        如果您使用连接等,您可能需要在选择中包含这些。请注意,为确保 foo 的 id 在 id 属性中,它需要最后包含。

        $builder = \App\Foo::select(['baz.*', 'foo.*']);
        

        将结果作为\App\Foo Eloquent 模型的集合正常检索。

        $foos = $builder->get();
        

        【讨论】:

          【解决方案5】:

          您不能以这种方式进行类型转换。
          您可以构建更改您的 Foo 类以获取对象的句柄并使用它。

          class Foo {
            private $object = null;
            public function __construct(stdClass $object) {
              $this->object = $object;
            }
          
            public function __get($property) {
              if (property_exists($this->object, $property)) {
                return $this->object->$property;
              }
            }
          
            public function __set($property, $value) {
              if (property_exists($this->object, $property)) {
                $this->object->$property = $value;
              }
              return $this;
            }
          
            public static function make(stdClass $object) {
              return new self($object);
            }
          
            public static function makeCollection(array $collection) {
              foreach($collection AS $key => $Item) {
                $collection[$key] = self::make($Item);
              }
              return $collection;
            }
          }
          
          $result = DB::table('foo')->get();
          $converted = Foo::makeCollection($result);
          

          【讨论】:

          • 这不起作用,因为Builder::get 返回一个 stdClass 对象数组,而不是单个对象。您可以遍历结果集,然后应用它来创建一个新的结果数组。
          • 那行得通。对于阅读本文的人,请记住您将在内存中保存两次结果集(在 $result 和 $converted 中),因此不要忘记清除 $result 并注意选择大型结果集。
          • @BenSwinburne,为什么? PHP 的内存管理运行良好(key => zval),它不会创建新的结果集,我的代码将用它的基于 Foo 的类对象替换项目并返回它。它只会用另一个 zval 替换,然后 GC 将清理未使用的 zval。如果我错了,我很乐意知道原因。
          • $result 包含 stdClass 对象数组,$converted 包含 Foo 对象数组。两者都包含查询的全部结果。
          • 好的,首先 $result 引用 stdClass 对象的集合,然后我们将它发送到 makeCollection,它已经迭代了具有相同引用的集合并替换为新的 Foo 的引用,但主要结果的引用是还是一样,stdClass 对象在垃圾中,然后 makeCollection 将 $result 的引用返回到 $converted。因此,$result 和 $converted 具有相同的引用。不是吗?我不落后于 PC,所以我现在无法检查它,但是,我很高兴讨论。 (:
          【解决方案6】:

          Laravel 5.8

          在你想转换到的类中(在这个例子中是YourClass)你需要这样的构造函数:

              /**
               * Use to initialize fields from DB query like:
               * $query->mapInto(YourClass::class)
               *
               * YourClass constructor.
               * @param \stdClass $data
               */
              public function __construct(\stdClass $data)
              {
                  foreach ($data as $key => $value) {
                      $this->$key = $value;
                  }
              }
          

          然后在您的查询中:

          $result = $query
          ->get()
          ->mapInto(YourClass::class);
          

          $result 现在将包含 YourClass 实例的集合,其中包含从查询中加载的字段。

          【讨论】:

          • 这是否比公认的解决方案更好?你能解释一下吗?
          【解决方案7】:

          是的。例如:

            $query = DB::table('example_tbl')->where('id', $id)->get();
            $cast = (array)$query;
            var_dump($cast);
          

          【讨论】:

          • 也许我应该更具体一些。这也适用于类吗?
          • Builder::get() 无论如何都会返回一个对象数组。 var_dump($query) === var_dump($cast)这里
          • 是的。但试着多看一点。它仍然是一个对象。要访问该值,您将使用此“->”。但是如果你转换一个数组,那么你可以使用这个“[]”来获取值。
          • @aldrin27 我已将其添加到问题中
          • @Oskar 我想你想从一个类中传递你的查询变量。对吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-14
          • 2019-12-29
          • 2012-07-05
          • 1970-01-01
          • 2015-06-12
          • 2010-10-31
          相关资源
          最近更新 更多