Я пытаюсь воссоздать Basecamp , используя Laravel, и я столкнулся с дизайном базы данных трех основных моделей User
, Account
и Project
.
Basecamp имеет концепцию под названием Project, которая может иметь одну из трех форм ( HQ , Team , Project ).
Этот вопрос будет разделен на 2 части:
- Часть 1 : Как лучше всего представить 3 типа модели проекта?
- Часть 2 : Как структурировать модели
Users
, Accounts
и Projects
?
Часть 1
Каждая учетная запись имеет:
- ровно один штаб.
- много проектов.
- много команд.
- клиенты могут иметь только проекты.
Так что Модель проекта может иметь несколько типов, способ, который я пытался найти, - это соотношение Laravel morphTo()
, введение typeable_type и typeable_id.
create_projects_table
Schema::create('projects', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('account_id');
$table->string('name');
$table->unsignedBigInteger('typeable_id')->nullable();
$table->string('typeable_type')->nullable();
$table->timestamps();
});
И создание базы модель
проект ctType. php
abstract class ProjectType extends Model
{
protected $guarded = [];
protected $table = 'projects';
protected static function boot()
{
parent::boot();
static::saved(function (Self $type) {
static::withoutEvents(function () use ($type) {
$type->associateType($type);
});
});
}
public function associateType(Self $type)
{
$this->typeable_id = $type->id;
$this->typeable_type = get_class($type);
$this->save();
}
public function typeable()
{
$this->morphTo();
}
}
Если вас интересует метод associateType()
, потому что мы использовали шаблон нулевого объекта, когда нам нужно создать модель, а затем назначить ее тип.
Теперь мы можем создавать 3 типа и расширяться от ProjectType
.
Мне кажется, я добавил больше сложности и головной боли для поддержки 3 моделей только потому, что правила
- Account Может иметь только один HQ.
- Типовые проекты - единственный тип, который позволяет клиентам.
Я уже знаю, что могу иметь поле enum в таблице проектов, но все еще удивляюсь. ..
Есть ли лучший способ, как я могу это реализовать?
Часть 2
Зная следующие отношения:
Пользователь Учетная запись «многие ко многим»
Проект «Учетная запись один ко многим»
Проект «Многочисленные пользователи»
Я хочу получать проекты пользователя через учетную запись
Auth::user()->accountProjects($accountId);
- Как лучше всего этого добиться?
- Это относится к Часть 1 : модель проекта, которую я получил, выполнив такой запрос, представляет собой либо отношение поворота, либо модель проекта, которая не удобна, если я пытался сохранить другой тип проекта. .
Зная, что я попробовал следующее:
ProjectType. php
<?php
class UserProject
{
private User $user;
private Account $account;
public function __construct(Account $account, $user = null)
{
if (is_null($user)) {
$user = Auth::user();
}
$this->user = $user;
$this->account = $account;
}
public static function forAccount(Account $account)
{
return (new static($account));
}
public function projects()
{
return $this->forType('App\Project');
}
public function hq()
{
return $this->forType('App\HQ');
}
public function teams()
{
return $this->forType('App\Team');
}
public function add(ProjectType $type)
{
$this->user
->accounts()
->attach($this->account, ['project_id' => $type->id]);
}
public function remove(ProjectType $type)
{
$this->user
->accounts()
->find($this->account->id)
->projects()
->wherePivot('user_id', $this->user->id)
->wherePivot('project_id', $type->id)
->detach();
return $this;
}
private function forType($type)
{
return $this->user->accounts
->find($this->account->id)
->projects()
->wherePivot('user_id', $this->user->id)
->where('typeable_type', $type)
->pluck('typeable_id', 'typeable_type')->map(function ($id, $type) {
return $type::find($id);
})->values();
}
public function all()
{
return $this->user->accounts
->find($this->account->id)
->projects()
->wherePivot('user_id', $this->user->id)
->pluck('typeable_id', 'typeable_type')->map(function ($id, $type) {
return $type::find($id);
})->values();
}
}