Laravel 4.2 Модели (часть 5)

Категория: Laravel

Все модели расположены в каталоге /app/models и  расширяют класс Eloquent (подробнее о Eloquent речь пойдет в следующей статье).  По умолчанию Laravel ожидает, что имя класса модели должно соответствовать единственному числу имени сущности (таблицы), например user → users. Но вы можете именовать и таблицы в единственном числе, указав в модели правильное имя таблицы.

Давайте создадим классы моделей User и Role, на основе которых я поведу повествование дальше. Рассмотрим случай при котором пользователь может иметь несколько ролей, т.е. присутствует связь Many-to-Many.

# app/models/User.php

/**
 * @property  string  $title  Не стесняйтесь декларировать magic свойства (поля таблицы)
 * @property  \Illuminate\Database\Eloquent\Collection|\Roles[]  $roles
 * @property  object          $some_field
 * @property  \Carbon\Carbon  $created_at
 * @property  \Carbon\Carbon  $updated_at
 * @property  \Carbon\Carbon  $deleted_at
 *
 * @method static \User firstOrFail(array $columns=[])
 * @method static \Illuminate\Support\Collection|\User findOrFail(mixed $id, array $columns=['*'])
 * @method static \User whereName(string $username)
 */
class User extends Eloquent
{
  // Для активации Soft Delete теперь используется trait вместо свойства `$softDelete`
  // Трэйт не может применяться к базовой модели! Только на реальной модели
  use SoftDeletingTrait;

  // Имя соединения с БД
  protected $connection = 'remote_dev';

  // Имя таблицы
  protected $table = 'user';

  // Если имя PK таблицы не id
  protected $primaryKey = 'id';

  // Разрешить массовое присваивание значений для этих полей
  protected $fillable = ['username', 'email', 'is_active'];

  // Запретить mass assigned для этих полей
  protected $guarded = ['id'];

  // Скрыть эти поля при экспорте методами toArray(),toJSON()
  protected $hidden = ['id', 'password'];

  // Mutated атрибуты, которые нужно выводить в toArray()
  protected $appends = ['accessor_field'];

  // Отключить использование timestamp полей
  public $timestamps = false;

  protected $dates = ['logged_at', 'deleted_at'];

  // Вызывается при инициализации модели
  public static function boot()
  {
    parent::boot();
    
    // Задать класс-наблюдатель, @see http://bit.ly/1kIexuO
    static::observe(new UserObserver());

    // Пример биндинга события (перед созданием)
    self::creating('User@generateAuthFields');
    self::saving([get_class(), 'onSavingSomeAction']);
  }

  public static function generateAuthFields($user)
  {
    if (!$user->uid) {
        $user->uid = md5(uniqid($user->email, true));
    }

    $user->token = md5(uniqid(rand(), true));
    $user->password = Hash::make($user->password);
  }

  public static function onSavingSomeAction($user) {}

  // Список полей, которые содержат дату (timestamp, datetime)
  // Эти поля будут преобразованы к типу Carbon\Carbon
  // @see https://github.com/briannesbitt/Carbon
  public function getDates()
  {
    return ['created_at', 'updated_at', 'active_to'];
  }

  // Формат даты и времени по умолчанию
  // При установке дефолтного формата для даты, вы можете столкнуться
  // с ошибкой: "Trailing data" - я долго не разбирался и удалил этот метод.
  protected function getDateFormat()
  {
    return 'Y-m-d H:i:s';
  }

  // ~ ~ ~ АКСЕССОРЫ, МУТАТОРЫ (accessor, mutator)

  // Выполняется при получении свойства модели (даже несуществующего)
  public function getSomeFieldAttribute($current_value) {
    // Можете вернуть значение другого поля: $this->other;
    return json_decode($current_value);
  }

  // Выполняется при установке свойства, в т.ч. методами create(), update(), fill()
  public function setSomeFieldAttribute($new_value) {
    $this->attributes['some_field'] = json_encode($new_value);
  }

  // ~ ~ ~ HELPERS
 
  public static function getOptions() {
    return self::orderBy('name')->get()->lists('name', 'id');
  }

  // ~ ~ ~ RELATIONS

  /**
   * Описание связи много ко многим
   * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
   */
  public function roles()
  {
    // (!) Имена связующих полей `user_id` и `role_id` будут генериться автоматически, 
    // по имени таблицы + окончание `_id`. Явно указывать эти поля необходимо лишь в случае,
    // если имя поля не соответствует этому формату, например имени таблицы.

    // (!) withPivot - извлечь доп. поля из связующей (relation, pivot) таблицы.
    // Эти поля будут доступны в свойстве pivot при экспорте (toArray, toJSON)

    // (!) withTimestamps - обновлять timestamp поля в pivot таблице при добавлении связи

    return $this->belongsToMany('Role', 'user_roles', 'user_id', 'role_id')
             ->withTimestamps() // указываем, что есть timestamp поля
             ->whereNull('user_group.deleted_at') // Кастомный фильтр (эмуляция Soft Delete)
             ->withPivot('created_at', 'updated_at', 'some_field');
  }

  /**
   * One To Many связь (юзер имеет много постов)
   * $user_posts = $user->posts;
   * $user_posts = $user->posts()->where(...)->first();
   * @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
   */
  public function posts()
  {
    return $this->hasMany('Post', 'user_id');
  }

  public function getRolesIds()
  {
    return $this->roles()->get()->modelKeys();
  }

  public function getChildren()
  {
    $ids = TableRelation::where('parent_id', $this->tableRelation->id)->lists('user_id');
    if (count($ids)) {
      return User::find($ids);
    }
    
    // Вернуть пустую коллекцию
    return new \Illuminate\Database\Eloquent\Collection([]);
  }
}

Модель  Role:

# app/models/Role.php
class Role extends Eloquent
{
  public function someModels()
  {
      return $this->hasManyThrough('Model1', 'Model1', 'id', 'model_id');
  }
}

Модель Post:

class Post extends Eloquent
{
  public function author()
  {
    // Связь Many-to-One
    return $this->belongsTo('User', 'user_id'); // post.user_id = user.id
  }

  public function author()
  {
    // Связь One-to-One
    return $this->hasOne('Meta', 'post_id'); // post.id = meta.post_id
  }

}

#Model, #Eloquent

категория: Laravel