Выяснение дизайна базы данных Basecamp - PullRequest
0 голосов
/ 25 апреля 2020

Я пытаюсь воссоздать 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();
    }
}
...