dimanche 3 avril 2016

Why does accessing a Facade in a Laravel constructor fail?

I'm currently trying to implement the repository pattern in Laravel.

I have an interface that declares the minimum methods required to be a valid repository.

<?php

namespace App\Repositories;

interface RepositoryInterface
{
    public function all();

    public function create($entity);

    public function update($entity);

    public function find($id);

    public function delete($entity);
}

I have an abstract class that represents interface methods on my repository that looks like this:

namespace App\Repositories;

use DB;

abstract class DbRepository
{
    public function all()
    {
        return DB::table($this->tableName)->get();
    }

    public function create($user)
    {
        return $user->save();
    }

    public function update($user)
    {
        return $user->update();
    }

    public function find($id)
    {
        return DB::table($this->tableName)->where('id', $id)->first();
    }

    public function delete($user)
    {
        return $user->delete();
    }
}

And an UserRepository that implements the interface and inherits from the abstract class like so:

namespace App\Repositories;

use App\Models\User;

class UserRepository extends DbRepository implements RepositoryInterface
{

    public $tableName;

    public function __construct($tableName = 'users')
    {
        $this->tableName = $tableName;
    }

    /**
     * Override the parent find method, as we want to return an instance of App\Models\User instead of stdClass
     * @param $id
     * @return mixed
     */
    public function find($id)
    {
        return User::where('id', $id)->first();
    }
}

Everything above works fine and my Unit Tests pass. However, I decided that it would be nice to hold the current database table as a property of the abstract implementation of the repository, so I refactored the class to look like this:

abstract class DbRepository implements RepositoryInterface
{
    private $table;

    public function __construct()
    {
        $this->table = DB::table($this->tableName);
    }

    public function all()
    {
        return $this->table->get();
    }
    # etc, etc...

And then made sure the abstract class' constructor was invoked from the Repository's constructor:

public function __construct($tableName = 'users')
{
    $this->tableName = $tableName;
    parent::__construct();
}

Upon doing this, I get the following error which I can't fix Error: Class 'DB' not found when running PhpUnit.

I know this error is to do with how I tried to refactor things. I believe it's to do with trying to access the DB Facade in a constructor of the abstract class (as accessing DB in other methods within the same class is fine). Any explanation would be much appreciated - I'm very curious as to why it doesn't work.

I have since reverted it to the original code (which was fine, really) and the tests are passing again!

Oh, and in case you want to check out my tests, here they are:

namespace Tests\Repositories;

use TestCase;
use App\Repositories\UserRepository;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class UserRepositoryTest extends TestCase
{
    use DatabaseMigrations;

    private $repository;

    public function __construct()
    {
        $this->repository = new UserRepository();
    }

    public function newUser()
    {
        return new User([
            'name' => 'Joe',
            'username' => 'joe',
            'password' => bcrypt('password'),
            'email' => 'joe@apple.com'
        ]);
    }

    public function test_all_method_returns_all_users()
    {
        $this->assertNotNull($this->repository->all());
    }

    public function test_create_user()
    {
        $this->assertTrue($this->repository->create($this->newUser()));
    }

    public function test_update_user()
    {
        // Arrange
        $user = $this->newUser();

        // Act
        $this->repository->create($user);
        $user->name = 'Jack';

        // Assert
        $this->assertTrue((integer)$this->repository->update($user) === 1);
    }

    public function test_delete_user()
    {
        // Arrange
        $user = $this->newUser();

        // Act
        $this->repository->create($user);

        // Assert
        $this->assertTrue((integer)$this->repository->delete($user) === 1);
    }

    public function test_find_user()
    {
        // Arrange
        $user = $this->newUser();

        // Act
        $this->repository->create($user);

        // Assert
        $this->assertInstanceOf(User::class, $this->repository->find($user->id));
    }
}



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire