Création d'un système de fichiers attachés

Voir la vidéo

Dans ce tutoriel je vous propose de mettre en place un système de fichiers attachés pour vos contenus. Le principe est d'offrir à l'administrateur du site la possibilité d'associer une ou plusieurs images à un article.

Sommaire

Structure

Avant de se lancer dans le code il est important d'avoir en tête le fonctionnement général de notre système. Pour gérer les fichiers attachés nous allons utiliser une relation polymorphique. Ce type de relation permet de créer une liaison multi-table (ce qui permettra d'attacher une image à plusieurs types de contenus). Du coup la table aura la structure suivante :

Schema::create('attachments', function (Blueprint $table) {
  $table->increments('id');
  $table->string('name');
  $table->string('attachable_type');
  $table->integer('attachable_id')->unsigned();
  $table->timestamps();
});

Ce qui donnera :

attachable_type attachable_id name
App\Post 3 demo.jpg
App\Tutoriel 2 php.jpg
App\Post 4 demo2.jpg

Feature Testing

Afin de s'assurer que tout fonctionne comme attendu nous allons décrire les différents comportements attendus à travers des tests fonctionnels. Vous pouvez utiliser ces tests pour essayer d'écrire le code par vous même avant d'attaquer la vidéo.

<?php

namespace Tests\Feature;

use App\Attachment;
use App\Post;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class AttachmentTest extends TestCase {

    public function setUp() {
        parent::setUp();
        Artisan::call('migrate');
        $this->cleanDirectories();
    }

    public function tearDown() {
        parent::tearDown();
        $this->cleanDirectories();
    }

    public function cleanDirectories () {
        Storage::disk('public')->deleteDirectory('uploads');
    }

    public function getFileForAttachment($attachment) {
        return dirname(__DIR__) . '/fixtures/uploads/' . $attachment['name'];
    }

    private function callController($data = []) {
        $path = dirname(__DIR__) . '/fixtures/demo.jpg';
        $file = new UploadedFile($path, 'demo.jpg', filesize($path), 'image/jpeg', null, true);
        $post = Post::create(['name' => 'demo', 'content' => 'demo']);
        $default = [
            'attachable_type' => Post::class,
            'attachable_id' => $post->id,
            'image' => $file
        ];
        return $this->post(route('attachments.store'), array_merge($default, $data));
    }

    public function testIncorrectDataAttachbleType () {
        $response = $this->callController(['attachable_type' => 'POOO']);
        $response->assertJsonStructure(['attachable_type']);
        $response->assertStatus(422);
    }

    public function testIncorrectDataAttachbleId () {
        $response = $this->callController(['attachable_id' => 3]);
        $response->assertJsonStructure(['attachable_id']);
        $response->assertStatus(422);
    }

    public function testCorrectData () {
        $response = $this->callController();
        $attachment = $response->json();
        $this->assertFileExists($this->getFileForAttachment($attachment));
        $response->assertJsonStructure(['id', 'url']);
        $this->assertContains('/uploads/', $attachment['url']);
        $response->assertStatus(200);
    }

    public function testDeleteAttachmentDeleteFile() {
        $response = $this->callController();
        $attachment = $response->json();
        $this->assertFileExists($this->getFileForAttachment($attachment));
        Attachment::find($attachment['id'])->delete();
        $this->assertFileNotExists($this->getFileForAttachment($attachment));
    }

    public function testDeletePostDeleteAllAttachments() {
        $response = $this->callController();
        $attachment = $response->json();
        factory(Attachment::class, 3)->create();
        $this->assertFileExists($this->getFileForAttachment($attachment));
        $this->assertEquals(4, Attachment::count());
        Post::first()->delete();
        $this->assertFileNotExists($this->getFileForAttachment($attachment));
        $this->assertEquals(3, Attachment::count());
    }

    public function testChangePostContentAttachmentsAreDeleted() {
        $response = $this->callController();
        $attachment = $response->json();
        factory(Attachment::class, 3)->create();
        $this->assertFileExists($this->getFileForAttachment($attachment));
        $this->assertEquals(4, Attachment::count());
        $post = Post::first();
        $post->content = "<img src=\"#{$attachment['url']}\"/> balbalbabla";
        $post->save();
        $this->assertEquals(4, Attachment::count());
        $post->content = "";
        $post->save();
        $this->assertEquals(3, Attachment::count());
        $this->assertFileNotExists($this->getFileForAttachment($attachment));
    }

    public function testChangePostContentAttachmentsAreDeletedIfImageChanged() {
        $response = $this->callController();
        $attachment = $response->json();
        factory(Attachment::class, 3)->create();
        $this->assertFileExists($this->getFileForAttachment($attachment));
        $this->assertEquals(4, Attachment::count());
        $post = Post::first();
        $post->content = "<img src=\"#{$attachment['url']}\"/> balbalbabla";
        $post->save();
        $this->assertEquals(4, Attachment::count());
        $post->content = "<img src=\"azeeaze/eazeazeazeaz/azeezaze.jpg\"/>";
        $post->save();
        $this->assertEquals(3, Attachment::count());
        $this->assertFileNotExists($this->getFileForAttachment($attachment));
    }

}
Publié
Technologies utilisées
Auteur :
Grafikart
Partager