Usar rasgos para las relaciones polimórficas en Laravel

Introducción


Hablemos sobre el posible uso de rasgos junto con las relaciones polimórficas en Laravel.


El contenido del artículo:


  1. Descripción del dominio
  2. Creación de aplicaciones
  3. Posibles estructuras de bases de datos
  4. Creación de entidad
  5. Uso de rasgos
  6. Pruebas de escritura

Descripción del dominio


Desarrollaremos un sistema en el que algunos empleados y ciertos equipos pueden unirse al proyecto. La esencia del área temática serán los empleados, los equipos y los proyectos: un equipo consta de empleados, los empleados y los equipos se pueden unir a un proyecto. Entre un equipo y un empleado, una relación de muchos a muchos (supongamos que un empleado puede participar en diferentes equipos), muchos a muchos entre proyectos y empleados, muchos a muchos entre equipos y proyectos. Para mayor consideración, omita la implementación de la comunicación entre el equipo y los empleados, centrémonos en la actitud de los equipos y los empleados hacia el proyecto.


Creación de aplicaciones


Las aplicaciones Laravel son muy fáciles de construir utilizando el paquete de creador de aplicaciones . Después de instalarlo, la creación de una nueva aplicación se ajusta a un comando:


laravel new system

Posibles estructuras de bases de datos


, : -, -, -.


, - - , (, 1 — , 2 — ).


- , — , .



, , , . :


php artisan make:model Employee -f //    
php artisan make:model Team -f //    
php artisan make:model Project -f //    
php artisan make:migration CreateEntitiesTables //     
php artisan make:model Attach -m //    

App/, database/migrations/ database/factories/.


. , : , . — .



<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateEntitesTables extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('employees', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });

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

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('employees');
        Schema::dropIfExists('teams');
        Schema::dropIfExists('projects');
    }
}


<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAttachesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('attachments', function (Blueprint $table) {
            $table->id();
            $table->morphs('attachable');
            $table->unsignedInteger('project_id');
            $table->timestamps();

            $table->foreign('project_id')->references('id')->on('projects')
                ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('attachments');
    }
}

, morphs().



:


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    protected $fillable = ['name'];
}

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Team extends Model
{
    protected $fillable = ['name'];
}


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Project extends Model
{
    protected $fillable = ['name'];

    /**
     * Relation for project attachments
     * @return HasMany
     */
    public function attachments()
    {
        return $this->hasMany(Attach::class);
    }

    /**
     * Relation for project employees
     * @return MorphToMany
     */
    public function employees()
    {
        return $this->morphedByMany(Employee::class, 'attachable', 'attachments');
    }

    /**
     * Relation for project teams
     * @return MorphToMany
     */
    public function teams()
    {
        return $this->morphedByMany(Team::class, 'attachable', 'attachments');
    }    
}


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Attach extends Model
{
    protected $table = 'attachments';
    protected $fillable = ['attachable_id', 'attachable_type', 'project_id'];
}


<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use Faker\Generator as Faker;

$factory->define(/* (//) */, function (Faker $faker) {
    return [
        'name' => $faker->colorName
    ];
});

, .



Laravel — morphedByMany(), — morphToMany(). , .


app/Traits : Attachable.php


<?php

namespace App\Traits;

use App\Project;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

trait Attachable
{
    /**
     * Relation for entity attachments
     * @return MorphToMany
     */
    public function attachments()
    {
        return $this->morphToMany(Project::class, 'attachable', 'attachments');
    }
}

use.


...
    use Attachable;
...

.



, Laravel PHPUnit . :


php artisan make:test AttachableTest

tests/Feature/. RefreshDatabase.


Verifique el morph en el lado del proyecto y el rasgo por parte del equipo y el personal


<?php

namespace Tests\Feature;

use App\Team;
use App\Employee;
use App\Project;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class OrderTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function polymorphic_relations_scheme(): void
    {
        // Given project
        $project = factory(Project::class)->create();
        // Given team
        $team = factory(Team::class)->create();
        // Given employee
        $employee = factory(Employee::class)->create();

        // When we add team and employee to project
        $project->teams()->save($team);
        $project->employees()->save($employee);

        // Then project should have two attachments
        $this->assertCount(2, $project->attachments);
        $this->assertCount(1, $project->teams);
        $this->assertCount(1, $project->employees);
        $this->assertEquals($team->id, $project->teams->first()->id);
        $this->assertEquals($employee->id, $project->employees->first()->id);
        // Team and employee should have attachment to project
        $this->assertCount(1, $team->attachments);
        $this->assertCount(1, $employee->attachments);
        $this->assertEquals($project->id, $team->attachments->first()->id);
        $this->assertEquals($project->id, $employee->attachments->first()->id);
    }
}

La prueba ha pasado!


Los rasgos le permiten no duplicar métodos comunes para las relaciones polimórficas dentro de las clases de modelos, también puede usarlos si tiene los mismos campos en muchas tablas (por ejemplo, el autor del registro); aquí también puede hacer un rasgo con el método de conexión.


Estaré encantado de escuchar sus casos de uso de rasgos en Laravel y PHP.


All Articles