Eloquent models - How do i break this dependancy - php

Take the following:
<?php
class EqUserRepository implements IUserRepository
{
//...
public function GetUserByID( $id )
{
return User::find( $id );
}
}
You can see that it returns a User
User is extending Model (Which extends Eloquent)
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
public function Profile()
{
return $this->hasOne( "Profile" );
}
Now imagine in our controller, we inject our concrete implementation via the interface:
class UserController extends Controller
{
private $userRepository;
public function __construct( \App\Models\Contracts\Repositories\IUserRepository $userRepo )
{
$this->userRepository = $userRepo;
All good so far!
Now we have a controller method that calls upon our injected repository and gets a User returned...
public function GetDetails( $id )
{
$user = $this->userRepository->GetUserByID( $id );
return view( 'user', array( "Model" => $user ) );
}
Now in our view we call upon the profile:
<p> Name: <?= $Model->Profile()->first_name ?> </p>
Oops, now our view is dependant on Eloquent because the Profile method on User is making calls using Eloquent.
I have been trying to figure out the best solution to break this dependancy.
I thought that having my repository map data into a User class that did not extend Eloquent was a good idea... but then you have some looping to map the classes which is a bit of a waste of time... especially considering you could just as easily use mysql_fetch_object( $query, "User" ) and have it map straight away.
So taking 1 into account, i thought the DB class must have a way of mapping direct to a type...
Have the User depend upon my IUserRepository, then my Profile function could look like this and that would certainly break the dependency, in a really good way!
.
public function Profile()
{
return $this->userRepository->GetProfileByUserID( $this->id );
}
But you ofcourse cannot DI into an eloquent model. I tried using the boot overriding method, but that doesn't like DI too, of course...
So, question time:
Can you set the return type of the DB class?
Can you somehow DI on an eloquent model?

Related

Is there a way to decouple Laravel Eloquent models from Service or Controller level?

There is a simple Laravel Eloquent Model below:
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
}
and it's normal to use repository pattern to work with model, like:
use Product;
class ProductRepository implement ProductRepositoryInterface
{
public function __construct(Product $model)
{
$this->model = $model;
}
public function findById($id)
{
return $this->model->find($id);
}
...
}
The controller use the repository to get Prodcut data:
class ProductController extends Controller
{
private $productRepository;
public function __construct(ProductRepository $productRepository)
{
$this->productRepository = $productRepository;
}
public function getSomeInfoOfProduct($id)
{
$product = $this->productRepository->findById($id);
return [
'name' => $product->name,
'alias' => $product->alias,
'amount' => $product->amount,
];
}
}
In the method getSomeInfoOfProduct, when I am deciding what kind of information should I return, I don't know there are how many properties the $product object has until I look at the schema of table products or migration files.
It's look like that the controller is tightly coupled with Eloquent models and the database. If one day, I store the raw data of products in Redis or other places, I still need to create a Eloquent model object, and fill in the object with the data from Redis.
So I am considering to create a pure data object to replace the Eloquent Model object, like below:
class ProductDataObject
{
private $name;
private $alias;
private $amount;
private $anyOtherElse;
public function getName() {
return $this->name;
}
....
}
and let the repository return this object:
use Product;
use ProductDataObject;
class ProductRepository implement ProductRepositoryInterface
{
public function __construct(Product $model)
{
$this->model = $model;
}
public function findById($id)
{
$result = $this->model->find($id);
// use some way to fill properties of the object
return new ProductDataObject(...);
}
...
}
In the controller or service level, I can just look at ProductDataObject to get all information I need. And it also looks like easier to change data storage without affecting the controllers and services.
Does this way make sense?
I think what you're looking for is the Factory Pattern. You're kind of on the right track already. Basically you have a middle-man class that your Controller or Repository basically asks to supply them with the appropriate Model. Through either parsing conditions or a config file using .envs, it figures out which one to serve up, so long as anything it returns all implements the same Interface.

Laravel Dependency Injection: When do you have to? When can you mock Facades? Advantages of either method?

I've been using Laravel for a while now and I have been reading a lot about Dependency Injection an testable code. I've come to a point of confusion when talking about Facades and Mocked Objects. I see two patterns:
class Post extends Eloquent {
protected $guarded = array();
public static $rules = array();
}
This is my Post Model. I could run Post::all(); to get all the posts from my blog. Now I want to incorporate it into my controller.
Option #1: Dependency Injection
My first instinct would be to inject the Post model as a dependecy:
class HomeController extends BaseController {
public function __construct(Post $post)
{
$this->post = $post;
}
public function index()
{
$posts = $this->posts->all();
return View::make( 'posts' , compact( $posts );
}
}
My unit test would look like this:
<?php
use \Mockery;
class HomeControllerTest extends TestCase {
public function tearDown()
{
Mockery::close();
parent::tearDown();
}
public function testIndex()
{
$post_collection = new StdClass();
$post = Mockery::mock('Eloquent', 'Post')
->shouldRecieve('all')
->once()
->andReturn($post_collection);
$this->app->instance('Post',$post);
$this->client->request('GET', 'posts');
$this->assertViewHas('posts');
}
}
Option #2: Facade Mocks
class HomeController extends BaseController {
public function index()
{
$posts = Post::all();
return View::make( 'posts' , compact( $posts );
}
}
My unit test would look like this:
<?php
use \Mockery;
class HomeControllerTest extends TestCase {
public function testIndex()
{
$post_collection = new StdClass();
Post::shouldRecieve('all')
->once()
->andReturn($post_collection);
$this->client->request('GET', 'posts');
$this->assertViewHas('posts');
}
}
I understand both methods but I don't understand why I should or when I should use one method over the other. For example, I've tried to use the DI route with the Auth class but it doesn't work so I have to use the Facade Mocks. Any calcification on this issue would be greatly appreciated.
Although you use dependency injection on Option #1, your controller is still coupled with the Eloquent ORM. (Note that i avoid to use the term Model here because in MVC the Model is not just a class or an object but a layer. It's your business logic.).
Dependency Injection allows for Dependency Inversion but they are not the same thing. According to the Dependency Inversion principle both high and low level code should depend on abstractions. In your case the high level code is your controller and the low level code is the Eloquent ORM that fetches data from MySQL, but as you can see none of them depends on abstractions.
As a consequence, you are not able to change your data access layer without affecting your controller. How would you go about changing for example from MySQL to MongoDB or to the File System? To do this you have to use repositories (or whatever you want to call it).
So create a repositories interface that all your concrete repository implementations (MySQL, MongoDB , File System etc.) should implement.
interface PostRepositoriesInterface {
public function getAll();
}
and then create your concrete implementation e.g. for MySQL
class DbPostRepository implements PostRepositoriesInterface {
public function getAll()
{
return Post::all()->toArray();
/* Why toArray()? This is the L (Liskov Substitution) in SOLID.
Any implementation of an abstraction (interface) should be substitutable
in any place that the abstraction is accepted. But if you just return
Post:all() how would you handle the situation where another concrete
implementation would return another data type? Probably you would use an if
statement in the controller to determine the data type but that's far from
ideal. In PHP you cannot force the return data type so this is something
that you have to keep in mind.*/
}
}
Now your controller must type hint the interface and not the concrete implementation. This is what "Code on an interface an not on implementation" is all about. This is Dependency Inversion.
class HomeController extends BaseController {
public function __construct(PostRepositoriesInterface $repo)
{
$this->repo= $repo;
}
public function index()
{
$posts = $this->repo->getAll();
return View::make( 'posts' , compact( $posts ) );
}
}
This way your controller is decoupled from your data layer. It's open for extension but closed for modification. You can switch to MongoDB or to the File System by creating a new concrete implementation of PostRepositoriesInterface (e.g. MongoPostRepository) and change only the binding from (Note that i don't use any namespaces here):
App:bind('PostRepositoriesInterface','DbPostRepository');
to
App:bind('PostRepositoriesInterface','MongoPostRepository');
In an ideal situation your controller should contain only application and not business logic. If you ever find yourself wanting to call a controller from another controller its a sign that you've done something wrong. In this case your controllers contain too much logic.
This also makes testing easier. Now you are able to test your controller without actually hitting the database. Note that a controller test must test only if the controller functions properly which means that the controller calls the right method, gets the results and pass it to the view. At this point you are not testing the validity of the results. This is not controller's responsibility.
public function testIndexActionBindsPostsFromRepository()
{
$repository = Mockery::mock('PostRepositoriesInterface');
$repository->shouldReceive('all')->once()->andReturn(array('foo'));
App::instance('PostRepositoriesInterface', $repository);
$response = $this->action('GET', 'HomeController#index');
$this->assertResponseOk();
$this->assertViewHas('posts', array('foo'));
}
EDIT
If you choose to go with option #1 you can test it like this
class HomeControllerTest extends TestCase {
public function __construct()
{
$this->mock = Mockery::mock('Eloquent', 'Post');
}
public function tearDown()
{
Mockery::close();
}
public function testIndex()
{
$this->mock
->shouldReceive('all')
->once()
->andReturn('foo');
$this->app->instance('Post', $this->mock);
$this->call('GET', 'posts');
$this->assertViewHas('posts');
}
}

How to map Laravel/Eloquent results to custom class

I'm looking for a way to map the results of Laravel / Eloquent database queries to custom classes, rather than the default Eloquent class.
Does Laravel / Eloquent include any built-in facility for doing this? If not, is there a suitable place to 'hook' into the result generation code and do the necessary mapping?
As an example, this is roughly what I'd like to achieve:
class User extends Eloquent {}
class MyUser
{
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
}
$users = User::all();
// $users should now contain an array of MyUser instances
Motivation / Reason for question
The motivation behind this question is to find a way in which queries can produce objects or arrays of objects that are completely framework-independent. This is because the Laravel app in question needs to be able to pass its results to other non-Laravel systems, so hence Plain Old PHP Objects (such as MyUser) make the most sense.
Laravel will not give you something like that, but you can do with PHP. Inject your Eloquent User class into your custom class, Laravel will inject it for you automatically. Use the object inside your class as you wish and, if you need to call one or another Eloquent method, you can just provide fallbacks to the Eloquent User object.
A good option is to use the repository pattern, where your class will expect to receive an implementation of a repository interface, for that you have to:
Create the interface for your user repository, all repositories, including your Eloquent model, must implement this interface. This is a contract to let you switch the implementation of the repository whenever you want, without having to touch your class. It also will make your class framework agnostic.
interface UserRepositoryInterface {
}
Your implementations of this repository could be:
class EloquentUser extends Eloquent implements UserRepositoryInterface {
}
class DoctrineUser extends DoctrineWhatever implements UserRepositoryInterface {
}
Create your class
class User extends Eloquent {}
class MyUser
{
protected $name;
public function __construct(UserRepositoryInterface $user)
{
$this->userRepository = $user;
}
public function __call($name, $arguments)
{
return call_user_func_array(array($this->userRepository,$name), $arguments);
}
public static function __callStatic($name, $arguments)
{
return call_user_func_array(array('User',$name), $arguments);
}
public function getName() {
return $this->userRepository->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
}
Now in Laravel to select the implementation you just have to
App::bind('UserRepositoryInterface', 'EloquentUser');
To switch to doctrine, you just have to
App::bind('UserRepositoryInterface', 'DoctrineUser');
And if you need to use this class outside Laravel, you just have to instantiate it, passing whatever implementation of the repository you want:
$user = new MyUser(new DoctrineUser);
No more ties to Laravel.
for Eloquent models, you might want to take advantage of ->toArray() method then typecast it using (object) to get POPO.
e.g.
$user = (object) User::find(1)->toArray();
print_r($user);
you should get:
stdClass Object
(
[id] => 1
[name] => John Doe
[email] => fakeuser0#mail.com
[active] => 1
...
[created_at] => 2017-10-26 17:45:53
[updated_at] => 2017-10-26 17:45:53
[deleted_at] =>
)
I also wanted to have custom class for this pre-defined class "User".
class User extends Model implements UserInterface, RemindableInterface {
to UserModel class:
class UserModel extends Model implements UserInterface, RemindableInterface {
So rather than hacking through the codes and classes, actually there is config for it.
Open app/config/auth.php
Make sure you change:
'model' => 'UserModel', // change to UserModel or your custom named class
'table' => 'User', // change if your table is non default "User"
It should be good to go.
:)

Laravel 4 setting up model using the IoC container

I recently watched this video and wanted to change my Laravel controllers so that they had their dependencies managed with Laravel's IoC container. The video talks about creating an interface for a Model and then implementing that interface for the specific data source used.
My question is: when implementing the interface with a class that extends Eloquent and binding that class to the controller so that it is accessible from $this->model, should I also create interfaces and implementations for the Eloquent models which may be returned when calling methods such as $this->model->find($id)? Should there be different classes for the Model and the ModelRepository?
Put it another way: how do I do new Model when my model is in $this->model.
Generally, yes, people doing that pattern (the repository pattern) have an interface which have some methods defined that your app will use:
interface SomethingInterface {
public function find($id);
public function all();
public function paged($offset, $limit);
}
Then you create an implementation of this. If you're using Eloquent, then you can make an Eloquent implementation
use Illuminate\Database\Model;
class EloquentSomething {
protected $something;
public function __construct(Model $something)
{
$this->something = $something;
}
public function find($id)
{
return $this->something->find($id);
}
public function all() { ... }
public function paged($offset, $limit) { ... }
}
Then you make a service provider to put it all together, and add it into app/config/app.php.
use Something; // Eloquent Model
use Namespace\Path\To\EloquentSomething;
use Illuminate\Support\ServiceProvider;
class RepoServiceProvider extends ServiceProvider {
public function register()
{
$app = $this->app;
$app->bind('Namespace/Path/To/SomethingInterface', function()
{
return new EloquentSomething( new Something );
});
}
}
Finally, your controller can use that interface as a type hint:
use Namespace/Path/To/SomethingInterface;
class SomethingController extends BaseController {
protected $something;
public function __construct(SomethingInterface $something)
{
$this->something = $something;
}
public function home() { return $this->something->paged(0, 10); }
}
That should be it. Apologies on any errors, this isn't tested, but is something I do a lot.
Downsides:
More code :D
Upsides:
Able to switch out implementations (instead of EloquentSomething, can use ArraySomething, MongoSomething, whatever), without changing your controller code or any code that uses an implementation of your interface.
Testable - you can mock your Eloquent class and test the repository, or mock your constructor dependency and test your controller
Re-usable - you can App::make() to get the concrete EloquentSomething anywhere in your app and re-use the Something repository anywhere in your code
Repository is a good place to add additional logic, like a layer of cacheing, or even validation rules. Stock mucking about in your controllers.
Finally:, since I likely typed all that out and STILL DIDN'T ANSWER YOUR QUESTION (wtf?!), you can get a new instance of the model using $this->model. Here's an example for creating a new Something:
// Interface:
public function create(array $data);
// EloquentSomething:
public function create(array $data)
{
$something = this->something->newInstance();
// Continue on with creation logic
}
Key is this method, newInstance().
I've used $newModel = $this->model and it's worked for me.

codeigniter instance of model class

I'm developing a site with codeigniter. Now, normally when you use a class in codeigniter, you basically use it as if it were a static class. For example, if I head a model called 'user', I would first load it using
$this->load->model('user');
and than, I could invoke methods on that user class like
$this->user->make_sandwitch('cheese');
in the application that I'm building, I would like to have one UserManagement class, which uses a class called 'user'.
so that, for example I could
$this->usermanager->by_id(3);
and this would return an instance of the user model where the id is 3.
What's the best way to do that?
The model classes in CI are not quite the same thing as model classes in other syntax's. In most cases, models will actually be some form of plain object with a database layer which interacts with it. With CI, on the other hand, Model represents the database layer interface which returns generic objects (they're kinda like arrays in some ways). I know, I feel lied to too.
So, if you want to make your Model return something which is not a stdClass, you need to wrap the database call.
So, here's what I would do:
Create a user_model_helper which has your model class:
class User_model {
private $id;
public function __construct( stdClass $val )
{
$this->id = $val->id;
/* ... */
/*
The stdClass provided by CI will have one property per db column.
So, if you have the columns id, first_name, last_name the value the
db will return will have a first_name, last_name, and id properties.
Here is where you would do something with those.
*/
}
}
In usermanager.php:
class Usermanager extends CI_Model {
public function __construct()
{
/* whatever you had before; */
$CI =& get_instance(); // use get_instance, it is less prone to failure
// in this context.
$CI->load->helper("user_model_helper");
}
public function by_id( $id )
{
$q = $this->db->from('users')->where('id', $id)->limit(1)->get();
return new User_model( $q->result() );
}
}
Use abstract factory pattern or even Data access object pattern which does the job that you require.
class User extend CI_Model
{
function by_id($id) {
$this->db->select('*')->from('users')->where('id', $id)->limit(1);
// Your additional code goes here
// ...
return $user_data;
}
}
class Home extend CI_Controller
{
function index()
{
$this->load->model('user');
$data = $this->user->by_id($id);
}
}

Categories