Fields in Laravel Eloquent models

Column names (fields) are not required in Eloquent models. As you pointed out, it is only necessary to define the functions which determine the relationships that a model has with others.

It isn't necessary to include them, because of the reason you mentioned (Laravel does a select * and then adds all of the returned rows to the model object as public properties). This is a process dubbed hydration and you can see exactly what is happening by digging into the Laravel source. Here's a summary of what happens:

  1. You call (for example), Users::find(123);
  2. Illuminate\Database\Eloquent\Model::find() calls Illuminate\Database\Eloquent\Builder::find()
  3. find() constructs the SELECT * FROM users WHERE id = 123 query and then returns the first result by calling Illuminate\Database\Eloquent\Builder::first()
  4. first() adds LIMIT 1 by calling Illuminate\Database\Query\Builder::take()
  5. Then first() sets the columns to be retrieved (* by default) by calling Illuminate\Database\Eloquent\Builder::get().
  6. get() returns an Illuminate\Database\Eloquent\Collection by using the return value of Illuminate\Database\Eloquent\Builder::getModels()
  7. getModels() actually performs the query and then calls Illuminate\Database\Eloquent\Model::newFromBuilder() for each row returned
  8. newFromBuilder() creates a new instance of the model and sets the columns (fields) by calling Illuminate\Database\Eloquent\Model::setRawAttributes()

I've omitted some unrelated things such as eager loading to simplify the process, but this is basically what happens for each query.

You make a good point that knowing the fields beforehand can be helpful for autocompletion. Because of the nature of setRawAttributes() it is perfectly OK to declare all column names (fields) in your model (just make sure they are public). The convention, though (and for you sanity), is to omit them. Such declarations should be left to migration files.

After further examination of the source, it is not ok to declare the fields beforehand. This is because the actual attribute values are stored in an $attributes property and then accessed by the magic method __get(). The trouble here is that by defining the properties beforehand, you will prevent __get() from being called when you access the fields. Therefore, this is not an option.

However, there are ways to hint to editors (like PhpStorm) about the existence of properties without explicitly defining them.


There is another way to make phpstorm to auto-complete column name and avoid warning.

/**
 * @property string $title Title of article
 */
class Article extends Eloquent