Skip to content

Model

  1. Use Model::query() instead of direct static calls:

    php
    // GOOD
    Member::query()->firstWhere('id', 42);
    
    // BAD
    Member::firstWhere('id', 42);
  2. Avoid mass assignment when possible:

    php
    // PREFERRED
    $member = new Member();
    $member->name = $request->input('name');
    $member->email = $request->input('email');
    
    // AVOID
    $member->forceFill([
        'name' => $request->input('name'),
        'email' => $request->input('email'),
    ]);
    
    // NEVER DO
    $member->forceFill($request->all());
  3. Don't use where{Attribute} magic methods. Use the where method to reduce magic.

  4. Document all magic using PHPDoc:

    php
    /**
     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Modules\Permission\Models\Role> $roles
     * @method static \Illuminate\Database\Eloquent\Builder|\App\Modules\Member\Models\Member canceled()
     */
  5. Use safe defaults for attributes:

    php
    final class CourseEnrollment extends Model
    {
        /** @var array<string, scalar|bool|null> Default values for Eloquent attributes */
        protected $attributes = [
            'graded_score' => 0,
            'potential_points' => 0,
            'completed_time_in_seconds' => 0,
        ];
    }
  6. Use custom EloquentBuilder classes for models with 3+ query scopes:

    php
    class User extends Model
    {
        #[\Override]
        public function newEloquentBuilder($query): UserEloquentBuilder
        {
            return new UserEloquentBuilder($query);
        }
    }
    
    /** @extends \Illuminate\Database\Eloquent\Builder<\App\Models\User> */
    final class UserEloquentBuilder extends Builder
    {
        public function confirmed(): self
        {
            return $this->whereNotNull('confirmed_at');
        }
    }
  7. Use invokable classes for reusable scopes:

    php
    $unverifiedUsers = User::query()
        ->tap(new UnverifiedScore())
        ->get();
    
    final class UnverifiedScore
    {
        public function __invoke(Builder $builder): void
        {
            $builder->whereNull('email_verified_at');
        }
    }