Skip to content

Instantly share code, notes, and snippets.

@babacarcissedia
Last active April 30, 2021 11:23
Show Gist options
  • Save babacarcissedia/034454f74edb332fd5ec2fae3a0809ab to your computer and use it in GitHub Desktop.
Save babacarcissedia/034454f74edb332fd5ec2fae3a0809ab to your computer and use it in GitHub Desktop.
TDD: My process when building API with laravel

Steps

  • Add this to the composer.json script section
        "lint": "vendor/bin/phpcs --ignore=database/migrations/** && composer lint2",
        "lint:fix": "vendor/bin/phpcbf && composer lint2:fix",
        "lint2": "vendor/bin/php-cs-fixer fix --config=.php_cs.dist -v --dry-run --using-cache=no",
        "lint2:fix": "vendor/bin/php-cs-fixer fix --config=.php_cs.dist -v --using-cache=no",
        "test": "vendor/bin/phpunit --configuration phpunit.xml",
        "test:feature": "vendor/bin/phpunit --configuration phpunit.xml --testsuite Feature --no-coverage",
        "test:unit": "vendor/bin/phpunit --configuration phpunit.xml --no-coverage --testsuite Unit",
  • Install php linter & code sniffer
composer require friendsofphp/php-cs-fixer squizlabs/php_codesniffer
  • Use uuid (optional. This is because I prefer working with UUID) Read the article
composer require dyrynda/laravel-model-uuid
  • Install spatie query builder
composer require spatie/laravel-query-builder
// Usage
<?php
  public function index (Request $request) {
  
  }
$items = QueryBuilder::for(Model::class)
    ->allowedIncludes([
    ])
    ->allowedFilters([
    ])
    ->allowedSort([
    ])
    ->jsonPaginate();
return ModelResource::collection($items);

Work flow for new resource

  • Create Category model
php artisan make:model Category -mfc

Notice how we're not implementing yet until we describe the expected results with tests.

php artisan make:test -u CategoryTest
<?php

namespace Tests\Unit\Models;

use App\Models\Category;
use App\Models\Post;
use Tests\TestCase;

class CategoryTest extends TestCase {

    public function testModel () {
        /** @var Category $post */
        $category = Category::factory()->create();
        $this->assertDatabaseHas('categories', [
           'id' => $category->id,
           'name' => $category->name
        ]);
    }
    
    public function testPosts () {
        $category = Category::factory()->create();
        $post = Post::factory()->create([
            'category_id' => $category->id
        ]);
        $this->assertEquals($category->posts()->first()->uuid, $post->uuid);
    }
}

  • Edit the migration Sample migration
<?php
        Schema::create('categories', function (Blueprint $table) {
           $table->increments('id');
           $table->string('name');
       });
    
  • Edit Category model

    • Annotate model with properties along with relationships
  • Create Post model

php artisan make:model Post -mfc
  • Write test for the model
php artisan make:test -u PostTest
<?php

namespace Tests\Unit\Models;

use App\Models\Category;
use App\Models\Post;
use Tests\TestCase;

class PostTest extends TestCase {
    public function testModel () {
        /** @var Post $post */
        $post = Post::factory()->create();
        $this->assertDatabaseHas('posts', [
           'id' => $item->id,
           'title' => $item->title,
           'category_id' => $item->category_id,
        ]);
    }
    
    public function testCategory () {
        $category = Category::factory()->create();
        /** @var Post $post */
        $post = Post::factory()->create([
            'category_id' => $category->id
        ]);
        $this->assertEquals($post->category()->id, $category->id);
    }
}
- Edit the migration
Sample migration
```php
<?php
        Schema::create('posts', function (Blueprint $table) {
           $table->increments('id');
           $table->string('name');
           $table->text('content');
           $table->integer('category_id')->index();
           // no foreign keys because I'm lazy but index for performance
       });
    

Sample relationship

<?php

namespace App\Models;


class Model {

    public function foo ()
    {
        return $this->belongsTo(Foo::class);
    }


    public function bars()
    {
        return $this->hasMany(Bar::class);
    }
}
  • Create Category resource test
<?php


namespace Tests\Unit\Http\Resources;

use App\Http\Resources\CategoryResource;
use App\Models\Category;
use Illuminate\Http\Request;
use Illuminate\Testing\Assert;
use Tests\TestCase;

class CategoryResourceTest extends TestCase
{

    public function testResource()
    {
        $item = Post::factory()->create();
        $resource = new PostResource($item);
        $expected = [
            "id" => $item->id,
            "name" => $item->name,
        ];
        $request = app()->make(Request::class);
        $result = $resource->toArray($request);
        Assert::assertArraySubset($expected, $result);
        // TODO test relationships
    }
}
  • Create Post resource
php artisan make:resource PostResource
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class PostResource extends JsonResource {
    public function toArray($request)
    {
        /** @var Post */
        $post = $this;
        return [
          'id' => $post->id,
          'name' => $post->name,
          'content' => $post->content,
          'category' => new CategoryResource($this->whenLoaded('category')),
          'bars' => Bar::collection($this->whenLoaded('bars')),
        ];
    }
}
  • Create Post resource test
<?php


namespace Tests\Unit\Http\Resources;

use App\Http\Resources\PostResource;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Testing\Assert;
use Tests\TestCase;

class PostTest extends TestCase
{

    public function testResource()
    {
        $item = Post::factory()->create();
        $resource = new PostResource($item);
        $expected = [
            "id" => $item->id,
            "name" => $item->name,
        ];
        $request = app()->make(Request::class);
        $result = $resource->toArray($request);
        Assert::assertArraySubset($expected, $result);
        // TODO test relationships
    }
}
  • Create Post resource
php artisan make:resource PostResource
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class PostResource extends JsonResource {
    public function toArray($request)
    {
        /** @var Post */
        $post = $this;
        return [
          'id' => $post->id,
          'name' => $post->name,
          'content' => $post->content,
          'category' => new CategoryResource($this->whenLoaded('category')),
          'bars' => Bar::collection($this->whenLoaded('bars')),
        ];
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment