Skip to content

Instantly share code, notes, and snippets.

@JeffreyWay
Last active December 11, 2015 20:08
Show Gist options
  • Save JeffreyWay/4652702 to your computer and use it in GitHub Desktop.
Save JeffreyWay/4652702 to your computer and use it in GitHub Desktop.
Testable Laravel.
<?php
class DogsController extends BaseController {
protected $db;
// Let the db-layer be injected. Don't worry...
// Laravel will automatically inject the dependencies for you.
function __construct(DogInterface $db) {
$this->db = $db;
}
public function index()
{
var_dump($this->db->getAll());
}
}
<?php
// When you need an instance of DogInterface,
// use the Eloquent implementation. Easy to switch out.
App::bind('DogInterface', 'EloquentDog');
// Let's define a contract for what any implementation
// of DogInterface should offer.
interface DogInterface {
public function getAll();
}
// Here's our first implementation: an Eloquent one.
class EloquentDog extends Eloquent implements DogInterface {
public $table = 'dogs';
public function getAll()
{
return static::all();
}
}
// And our second implementation, perhaps for a simple Array "DB"
class ArrayDog implements DogInterface {
public function getAll()
{
return ['sparky', 'trash', 'ham'];
}
}
route::get('/', function() {
// Laravel is smart enough to inject the necessary
// dependencies for, using reflection.
$dog = App::make('DogInterface');
// Try changing line 5 to the second implementation, ArrayDog
// Instance switch!
$dog->getAll();
});
@johnnncodes
Copy link

Hi Jeffrey, thanks for sharing this gist. Anyway I got a question. Is it better to extend eloquent and implement the interface at the same time like your code above like this:

class EloquentDog extends Eloquent implements DogInterface {}

Or it would be better to create 1 class that implements the interface and 1 class that extends eloquent like this:

class EloquentDog implements DogInterface {
public function all() {
return Dog::all();
}
}

class Dog extends Eloquent {}

Because I'm confused, some said the second option is better. My question is related to this one - laravel/framework#139 (comment)

Wanna know your thoughts about it. Thanks.

@taylorotwell
Copy link

I think Jeff's example is OK as long as your repository doesn't have any dependencies. However, if your repositories start to have dependencies you would need to split the repository from the eloquent model itself, with something like:

interface DogRepositoryInterface {}

class EloquentDogRepository implements DogRepository {}

class Dog extends Eloquent {}

Then you would inject an implementation of DogRepositoryInterface into your controllers.

@eoconnell
Copy link

I believe the IoC container will throw an exception when it tries to resolve the dependency in the constructor for DogController. It should complain that it cannot resolve type Array in the Eloquent constructor.

I ran into this issue when I trying my own approach: https://gist.github.com/4367545

@JeffreyWay
Copy link
Author

John - Yeah, exactly what Taylor said. I was trying to keep it somewhat simple for this Gist, but yeah, I'd imagine that in most projects, you'd want a base Dog class, and then have Laravel automatically inject the dependency.

I'll update this Gist in a sec.

@johnnncodes
Copy link

Thank you very much to all of you for taking time to respond to my question. I now understand why sometimes its better to separate it depending on the situation. Thanks again. Cheers everyone!

@JeffreyWay
Copy link
Author

Evan - Hmm - I checked this code before posting the Gist, and didn't notice any exceptions being thrown...

@johnnncodes
Copy link

@eoconnell - You should update your repo to the latest one. do composer update

That issue is already solved here - laravel/framework#80

@taylorotwell
Copy link

Yeah, the IoC container will resolve default arguments now, so shouldn't get any exceptions here.

@trq
Copy link

trq commented Jan 28, 2013

The idea of a Dog being an extension of Eloquent makes little sense. A dog is not a type of database abstraction.

@eoconnell
Copy link

Great to hear. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment