Как создать оптимизированную модель, таблицы и отношения между ними?

У меня есть собственный проект разработки, в котором есть таблицы базы данных:

  • пользователи
  • кандидаты
  • компании
  • объемы
  • навыки и умения

Таблица пользователей:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('nickname')->unique();
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->boolean('type')->default(0); // 0 = candidate and 1 = company
    $table->rememberToken();
    $table->timestamps();
});

Таблица кандидатов:

Schema::create('candidates', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')
          ->references('id')
          ->on('users')
          ->onDelete('cascade');
    $table->string('name')->nullable();
    $table->string('lastname')->nullable();
    $table->string('surname')->nullable();
    $table->text('about')->nullable();
    $table->timestamps();
});

Таблица компаний:

Schema::create('companies', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')
          ->references('id')
          ->on('users')
          ->onDelete('cascade');
    $table->unsignedInteger('logo');
    $table->foreign('logo')
          ->references('id')
          ->on('images')
          ->onDelete('cascade');
    $table->string('name')->nullable();
    $table->text('about')->nullable();
    $table->string('website')->nullable();
    $table->integer('employees')->nullable();
    $table->timestamps();
});

Таблица областей действия:

Schema::create('scopes', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name')->unique();
    $table->timestamps();
});

Таблица навыков:

Schema::create('skills', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name')->unique();
    $table->timestamps();
});

Здесь у любого типа пользователей есть возможности и навыки. Я могу решить это решение, создав одну таблицу:

Таблица атрибутов:

Schema::create('attributes', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')
          ->references('id')
          ->on('users')
          ->onDelete('cascade');
    $table->morphs('model');
    $table->timestamps();
});

Пример данных в таблице атрибутов:

----------------------------------------
| id | user_id | model_id | model_type |
----------------------------------------
| 15 |   176   |  34458   | App\Scope  |
----------------------------------------
| 29 |   245   |  17654   | App\Skill  |
----------------------------------------

Но в этом решении для меня есть одна плохая сторона, это повторение строкового имени модели в каждой строке записи в таблице. Например:

Таблица моделей:

Schema::create('models', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name')->unique();
    $table->timestamps();
});

Примечание. Может оказаться невозможным создать модуль с именем Модель, поэтому вам может потребоваться создать его с другим именем, например: Ссылка

Пример данных в таблице models:

-------------------
| id |    name    |
-------------------
| 10 | App\Scope  |
-------------------
| 11 | App\Skill  |
-------------------

После создания таблицы моделей структура таблицы атрибутов изменится на:

Таблица атрибутов:

Schema::create('attributes', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')
          ->references('id')
          ->on('users')
          ->onDelete('cascade');
    $table->foreign('model_id')
          ->references('id')
          ->on('models')
          ->onDelete('cascade');
    $table->unsignedInteger('content_id');
    $table->timestamps();
});

Теперь, как я могу создать правильные отношения внутри моделей, чтобы получить у кандидатов или компании навыки работы с типами пользователей или их объем?

У меня есть собственные отношения внутри модели пользователей:

public function candidate()
{
    return $this->hasOne(Candidate::class);
}

public function company()
{
    return $this->hasOne(Company::class);
}

Теперь мне нужны такие отношения внутри кандидата и компании:

public function skills()
{
    // Some code here
}

public function scopes()
{
    // Some code here
}

Как правило, получайте навыки или возможности пользователей следующим образом:

$user->candidate->scopes
$user->company->skills

Если у вас есть другое предложение по решению моей проблемы, пожалуйста, предложите свое предложение. Если где-то ошибся, поправьте меня, если можно.


person Andreas Hunter    schedule 21.10.2018    source источник
comment
Вы пытались создать отношения 1 ко многим между кандидатом и областью действия и компанией с навыками? кандидат- ›hasMany (scope :: class, 'внешний ключ', 'primarykey'); аналогично компания и навыки?   -  person Amir Parvez    schedule 21.10.2018


Ответы (1)


Подумайте, ваша основная ошибка построения ORM находится в этой строке:

$table->boolean('type')->default(0); // 0 = candidate and 1 = company

Это не решается с использованием ORM Laravel. Вам нужно будет заменить эту строку на

$table->unsignedInteger('candidate_id')->nullable()->index();
$table->unsignedInteger('company_id')->nullable()->index();

и начнем оттуда. Так как это может быть кандидат или компания, одно из значений будет пустым. Легко проверить. Поскольку есть только 2 возможных варианта, полиморфные отношения на самом деле не нужны. Как только вы пройдете эту проверку, ваша иерархия ORM очертится.

Вам нужно только позаботиться о том, чтобы если кандидат становится компанией или наоборот, вы убедитесь, что другое значение обнуляется. Все это можно сделать в мутаторах модели или в персонализированной общедоступной функции, поэтому вам не нужно беспокоиться об этом в контроллерах / представлениях.

Например $user->choice->scopes;, где вы добавляете функцию модели пользователя getChoiceAttribute()

public function getChoiceAttribute() {
  if ($this->candidate) {
    return $this->candidate;
  }
  if ($this->company) {
    return $this->company;
  }
  return null;
}

То же самое относится к функции установки setChoiceAttribute() в случае, если кандидат становится компанией. Это применимо только в том случае, если вы сохраняете или обновляете запись.

Чтобы узнать в представлении, работаете ли вы с кандидатами или компаниями, просто проверьте $user->choice->getTable();.

person Dimitri Mostrey    schedule 21.10.2018