Cast Laravel query results to class

前端 未结 6 1378
清酒与你
清酒与你 2021-02-02 12:04

When creating a query using the syntax DB::table(\'foo\'), it creates a generic class (stdClass). Is there any way to cast the resulting rows to a specific class?

相关标签:
6条回答
  • 2021-02-02 12:29

    Typically you'd achieve this by setting the PDO Statement fetch_style to PDO::FETCH_CLASS as below

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

    If you look at the method Illuminate\Database\Connection::select you'll see that whilst you can set the fetch_style/fetchMode, you can not the second argument.

    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());
        });
    }
    

    Nor can you get access to the statement before fetchAll is called to call PDOStatement::setFetchMode for example.

    You could perhaps attempt to extend Illuminate\Database\Connection and utilise that throughout other Database related classes by extending and replacing where necessary but it seems like a hefty task to maintain.

    The other option is to use Eloquent which will give you classes back of a particular type but you get the slight additional overhead of hydrating the model objects.

    class Foo extends Illuminate\Database\Eloquent\Model {
        protected $table = 'foo';
    }
    
    Foo::all()
    Foo::where('col', 1)->get()
    
    0 讨论(0)
  • 2021-02-02 12:32

    As of laravel 5.4

    Eloquent models dont have the hydrate() method.

    This has been moved to the Eloquent Builder class.

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

    The Eloquent Builder class requires a few things to get this working manually

    1. A query builder to initialize
    2. A model to hydrate with.

    Here is my working example in some laravel 5.8 code:

                $instance = new $class;
                $table = $instance->getTable();
    
                /** @var \Illuminate\Database\Eloquent\Builder $eloquent_builder*/
                $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();
    
    0 讨论(0)
  • 2021-02-02 12:35

    Yes, you can hydrate the results into the classes you want. I found the answer buried deep in a mess of half-answers and confused questions that make up the terrible Laracasts.com forum. Thanks for asking the question here instead of there.

    Once you get the results, hydrate them using your model class:

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

    Edit: Found some documentation on the hydrate method too, if that helps

    Edit 2: I found myself in a situation where I needed to cast results from either an array or a collection, depending on the results of a query. When a collection was returned, it was correctly hydrated, but when the result was an array, they were just stdClass. I wrote a quick method added to my master model that took a collection of arrays or objects, or a pagination object, and correctly cast it to the object I wanted.

    0 讨论(0)
  • 2021-02-02 12:39

    Laravel 5.8

    In the class you want to cast to (YourClass in this example) to you'll need constructor like this:

        /**
         * 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;
            }
        }
    

    Then in your query:

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

    $result will now contain a collection of YourClass instances with fields loaded from the query.

    0 讨论(0)
  • 2021-02-02 12:39

    Yes. For example:

      $query = DB::table('example_tbl')->where('id', $id)->get();
      $cast = (array)$query;
      var_dump($cast);
    
    0 讨论(0)
  • 2021-02-02 12:42

    You cannot type cast it this way.
    You can build change Your Foo class to get handle of object and work with it.

    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);
    
    0 讨论(0)
提交回复
热议问题