I want to write discrete framework agnostic models.
I wrote interfaces for all of these models.
The problem is when implementing these interfaces, for example with Eloquent I'm linking all my business logic into the ORM.
For example I want a method addVariation on a Product model.
The Interface
interface ProductInterface
{
/**
* Adds a variation
*
* #param VariationInterface $variation
*/
public function addVariation(VariationInterface $variation);
// ...
}
The Concretion
class Product extends Model
{
/**
* #param Collection | VariationInterface
*/
protected $variations;
public function addVariation(VarientInterface $varient)
{
if( ! $this->hasVariation($varient) )
{
$this->variations->add($variations);
}
return $this;
}
}
The problem I have is all my business logic lives in my specific Eloquent ORM implementation of my model.
How could I possibly separate this out? The only real dependancy I can see is I need a collection class of some type? or maybe I can just use plain old arrays?
I just dont want to link all my logic into a specific ORM I want to remain framework agnostic.
Just remove all your logic from the Eloquent ORM.
You only need an ORM to make saving and retrieving data from a database easier. You should write all your business logic with plain old php objects. You can then create some general PersistenceGateway interface that all your business logic models use e.g.
interface PersistenceGatway {
public function saveGatewayData($id, array $data);
public function retrieveGatewayData($id)
}
Your decoupled business logic uses this interface to save and retrieve data. Then all you need to do is implement the interface with your ORM of choice (or you may need to also create some adaptor class to help you). You can now plugin any ORM you like, so long as it implements the PersistenceGateway interface.
Take a look at Uncle Bobs Clean Architecture. Web frameworks like Laravel should be a plugin to your app/business logic, not the other way around.
Edit: Very basic example.
class SomeBusinessLogic {
// Note: Class depends on the PersistenceGateway. Any class
// that implements this interface can be passed in. This is
// essentially how you decouple logic from ORMS. You can now
// implement this interface with any ORM you like and pass it in
public __construct(PersistenceGateway $gateway){
$this->gateway = $gateway;
}
public function someLogicMethod($id){
// do something and save state to the gateway
$this->gateway->saveGatewayData($id, ['some_state'=>'value']);
}
public function someDataReturnMethod($id){
return $this->gateway->retrieveGatewayData($id);
}
}
// Im extending from Eloquent here, but you can do a similar thing
// with any ORM.
class SomeEloquentModel extends Eloquent implements PersistenceGateway {
public function saveGatewayData($id, array $data){
$model = $this->find($id);
// call eloquent save method
$model->save($data);
}
public function retrieveGatewayData($id){
// retrieve the data. Important to return
// an array NOT an eloquent model, otherwise
// we are still coupled. I think toArray() is
// the correct method to call on eloquent model
return $this->find($id)->toArray();
}
}
class SomeController {
class someControllerMethod {
// Your controller decides on the implementation of PersistenGateway
// to use. You could also use an IoC container which would be a slightly
// cleaner solution
$logic = new SomeBusinessLogic(new SomeEloquentModel());
$logic->someLogicMethod(Input::get('id'));
return $logic->someDataReturnMethod(Input::get('id'));
}
}
Related
I read some articles about repository pattern and I want to know the reason why the constructor is needed when I can directly call the Model and return the data? I also think that Book::all(); is less code than $this->model->all(). Is it just a good practice or it has some purpose?
class BookRepository implements RepositoryInterface {
private $model;
public function __construct(Book $model)
{
$this->model = $model;
}
public function index()
{
return $this->model->all();
}
}
and
class BookRepository implements RepositoryInterface {
public function index()
{
return Book::all();
}
}
The primary reason is Inversion of Control, basically letting your application determine what should be provided to fulfill that dependency. The reason this is important is, in the event you decide to refactor that code, you can simply tell Laravel to load a different implementation. No code need be altered in the Repository itself.
This however leads into the idea of not using classes directly, and using interfaces instead to declare your dependancies. That way any implementation can be swapped out and your code remains readable.
class BookRepository {
public function __construct(BookInterface $book)
{
$this->book = $book;
}
}
Now your Repository doesn't really care about the actual class, just that it implements the book interface which enforces a specific set of methods be defined. An example of the benefit is if you're using, say, MySQL as a database for your Book but switch to Postgres you may need to significantly change the underlying code but want to keep both implementations for legacy reasons. You can easily tell Laravel to load your standard Book class, or your new PostgresBook class because both still implement the BookInterface.
Your Repository doesn't need to change at all. Just add a bind and you're good.
Another more direct example is if you decided you wanted to switch from Eloquent to ActiveRecord.
Both will work but if for any reason you want to change the model class [Book] with any other model for example [MyBook] so in this case, you will change only the constructor parameter, not all the functions which use [Book]
public function __construct(MyBook $model)
{
$this->model = $model;
}
My goal of asking this question is to ferret out whether there are benefits to injecting Controller directly with the data it needs (more specific approach) opposed to injecting a Model into a Controller (more generic approach). Or to establish whether or not it is just a matter of preference.
Injecting Controller with Model:
Model can be used to run all kinds of queries to retrieve various bits of data, but it is a heavier-weight construct than the data itself. Model essentially contains data, or at least it can access all the data you may need. Example:
class CategoryControllerWithModel
{
private $model;
public function __construct($model)
{
$this->model = $model;
}
// generates HTML for input form
public function genHtml()
{
/* retrieve data */
$categories = $this->model->getCategories();
//...
}
}
//instantiation within Factory Method:
class Factory
{
$model = new CategoryModel();
$controller = new CategoryControllerWithModel($model);
return $controller;
}
Injecting Controller with Data:
Here we do a bit more upfront with in the Factory method but we get a leaner Controller that only receives exactly the data it needs and is so completely separated from the Model that it is not even aware of its existence.
class CategoryControllerWithData
{
private $categories;
public function __construct($categories)
{
$this->categories = $categories;
}
public function genHtml()
{
$categories = $this->categories;
}
}
//instantiation within Factory Method:
class Factory
{
$model = new CategoryModel();
//a bit more work to get the data Controller needs
//benefit: Controller not tied to the Model
$categories = $model->getCategories():
$controller = new CategoryControllerWithData($categories);
return $controller;
}
Question:
I suppose MVC stands for exactly that -- Model, View, Controller, so injecting Model is probably considered to be an "okay" thing to do. If so, am I taking this too far by trying to remove Controller dependency on Model?
Suppose I insist that I want to inject Data into my Controllers rather than the Model. Is this a purely preferential issue do you see any concrete benefits of doing so?
From my point of view, Factory shouldn't be responsible for domain logic. It should only be responsible for building things up.
In this case, where you are injecting data, Factory has to know what categories controller is searching for, are there any filtering and so on.
So I think for controller you should only inject model, keep Factory single responsibility only for building things and controller should be responsible for it's data.
I think it's a matter of "separation of concerns" also I do not think that would be a good example of using MVC. I would think more along these lines:
class FooController
{
public function actionView($alias){
$category = Category::loadByAlias($alias);
..... load and render layouts etc .....
}
public function actionList(){
$categories = Category::loadAll();
..... etc ......
}
}
like this the neither the Controller nor the Factory need to know what needs to be done when you load a category nor do they have to handle active/inactive status, even User access ... etc this is all Model Logic, Model can have beforeLoad and afterLoad functions, conditions for listing all categories, eager or lazy loading of related models etc...
Read books From Apprentice To Artisan and Implementing Laravel by Chris Fidao and now i don't know how to correctly work with Models in Repositories.
In Implementing laravel book author is working with models in this way:
Example #1
<?php
use MyApp\Interfaces\UserInterface;
use Illuminate\Database\Eloquent\Model;
class UserRepository implements UserInterface
{
protected $user;
public function __construct(Model $user)
{
$this->user = $user;
}
public function find($userId)
{
return $this->user->find($userId);
}
}
But that can by done in other way, not injecting Model as a dependency, like this:
Example #2
Built example using tutorial http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories/
<?php
use MyApp\Interfaces\UserInterface;
use MyApp\Models\User\User;
class UserRepository implements UserInterface
{
public function find($userId)
{
return User::with('profile')->find($userId);
}
}
Why in first example Model is injected, why not use directly Model like in example two?
Which way is correct and why ?
Also which way will be more testable with integrated to laravel UnitTest package ?
The example 2 is bad because it's coupling your repository to a particular implementation of the User model.
Every time you use your repository it'll need to instantiate Univemba\Models\User\User. The whole idea of Dependency Injection is to inject (send) in to your object whatever dependencies it has. If your object needs a Model to work with you can send it a Laravel Eloquent Model, but any of your co-workers could also need to send to it a Doctrine Model. But if you couple your class to Eloquent, this isn't possible.
So in the first example, there is no instantiation happening on your code and it's not using a concrete class directly as in the second:
return User::with('profile')->find($userId);
It is receiving an implementation in the process of its instantiation:
public function __construct(Model $user)
{
$this->user = $user;
}
There are better ways to do that, because it is still expecting a concrete class while it should be expecting an implementation of an interface
public function __construct(ModelInterface $user)
{
$this->user = $user;
}
In this case you just need to pass to your object something that implements ModelInterface, which could be
Univemba\Models\EloquentModel
Or
Univemba\Models\DoctrineModel
Because both would be implementing
Univemba\Models\ModelInterface
I think if a Repository is intended to be the Eloquent implementation of a RepositoryInterface it isn't a bad idea to use the EloquentModel directly.
What is "the right way" to implement business logic Models with ZF2 and Doctrine, while creating a clean OOP and MVC structure, and providing the Models with access to the EntityManager?
Althought I am tempted to use the Doctrine Repositories for all the business logic, I know they should primarily be used to perform complex queries. I also know that a model in ZF2 can be provided with dependencies (EntityManager) in several ways: ZF2: Dependency injection done the proper way http://zend-framework-community.634137.n4.nabble.com/ZF2-Injecting-objects-to-a-controller-or-getting-objects-from-the-service-locator-td4656872.html
To illustrate the problem in real life, how do we create a Model layer for a simple Web shop functionality:
Firstly, we would have the Entities
/**
* #Entity(repositoryClass="App\Repository\ProductRepository")
* #Table(name="products")
*/
class Product { ... }
/**
* #Entity(repositoryClass="App\Repository\ProductgroupRepository")
* #Table(name="productgroups")
*/
class Productgroup { ... }
And the Repositories which give us more detailed query capabilities
class ProductRepository extends EntityRepository {
public function isAvailable() { ... }
public function getQuantity() { ... }
public function getProductImages() { ... }
public function getRelatedProducts() { ... }
...
}
class ProductgroupRepository extends EntityRepository {
public function getAvailableProducts() { ... }
public function getAllProducts() { ... }
...
}
But where do we place the business logic such as this?
Products functionalities:
- createNewProduct( $data ), eg. update prices, retrieve new quantities, generate a new image gallery, notify the administrator, ...
- deleteProduct(), eg. delete corresponding files and existing associations, put product into archive
- ...
Productgroups functionalities:
- removeProductFromProductgroup( $product), eg. update ordering in group, remove associations, ...
- addProductToProductgroup( $product ), eg. update ordering in group, create associations, ...
- deleteProductgroup(), eg. delete productgroup, set all it's products as uncategorized
- ...
Should the Productgroup business model be created, for example, as a class which is injected with the EntityManager at the service level? --
class Productgroup implements ServiceLocatorAwareInterface
{
public function removeProductFromProductgroup( $productgroup, $product) { }
public function addProductToProductgroup( $productgroup, $product) { }
}
Or should it maybe also extend the original entity so as to have access to its internal structure? --
class Productgroup extends \Application\Entity\Productgroup
implements ServiceLocatorAwareInterface
{
public function removeProductFromProductgroup( $productgroup, $product) { }
public function addProductToProductgroup( $productgroup, $product) { }
}
And if so, should it also have some kind of a set state method ?
public function set( $product ) {
$this->populate( $product );
}
About where to have Business Logic, it is obviously Models. I would not recommend extending your Entities because any logic which has nothing to do with setting/getting your entity object should be outside your entity.
So the answer is yes, I would be injecting the Entity Manager into the models. But whether you use the Service Locator or some other method for DI is upto you.
The questions you are pondering over is really about OOP, DI, DiC, etc. Please go through some of the best blog posts on DI from the gurus. I have listed them below:
Inversion of Control
Martin Fowler's Inversion of Control Containers and the Dependency Injection pattern
Learning About Dependency Injection and PHP
What is Dependency Injection?
Its really upto you to take your dependency injection to different levels. If you check Martin Fowler's blog, you can find out about constructor injection, setter injection and a DiC doing the injection for you. You have to take the call for your system. What is working for me is, for within module dependency, I do constructor injection (so that the dependency is clearly visible). Anything that is a service, will be called from outside my module will be through a DiC or ServiceLocator.
The only argument against the Service Locator pattern is it hides the dependency injection into its configuration and it is not clearly visible.
I use the Array Collection for tasks like these. This Object has various methods that are pretty usefull like clean(), contains() etc.
I tend to store my oneToOne, oneToMany etc. relations in arrayCollections.
Within your Entity you'll have to use a constructor to initiate the collection.
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\OneToMany(targetEntity="yourModule\Entity\yourEntity", mappedBy="yourRelation", cascade={"persist"})
*/
protected $yourCollection;
public function __construct()
{
$this->yourCollection = new ArrayCollection();
}
//once I have the collection I usually create methods like add, remove, etc methods like so:
public function addToCollection($toAdd) {
$this->yourCollection->add($toAdd);
}
public function removeFromCollection($toRemove) {
$this->yourCollection->removeElement($toRemove);
}
//etc.....
I don't get it how it really works.
I have a database table user and a model User:
<?php
class User extends Zend_Db_Table_Abstract {
protected $_name = 'users';
}
And in the controller I call it: $user = new User()
But how do I get for example all users or a user with the id: 1?
Zend_Db_Table implements the Table Gateway pattern and the associated Zend_Db_Table_Row implements the Row Data Gateway pattern.
This means that as the table class represents your database table, it makes more sense to use a plural class name. Similarly, a singular class name should be used for the row class.
Hence you end up with something like this:
class Model_Users extends Zend_Db_Table_Abstract
{
protected $_name = 'users'; // database table name
protected $_rowClass = 'Model_User'; // row class name
public function fetchAllInLastNameOrder()
{
return $this->fetchAll(null, array('last_name', 'first_name'));
}
public function fetchUserById($id)
{
return $this->_fetchRow('id = '. (int)$id);
}
}
class Model_User extends Zend_Db_Table_Row_Abstract
{
public function getFullName()
{
return trim($this->title . ' '
. $this->first_name . ' '
. $this->last_name);
}
}
The point of creating your own classes is that you can add your own methods that your controllers and views can then use. You should never use the Zend_Db_Table methods directly other than in your model classes. This is because this model design is tightly coupled to the database implentation. Over time, you may find that this isn't flexible enough and want to use a different system. If you have ensured that the rest of your application only ever accesses methods created in Model_Users and Model_User then you should be able to reimplement your model without breaking the rest of your app...
For larger applications and ones with complex business logic, it is rare for a model to be a simple wrapper over a single database table. For better flexibility and maintainability, you can consider creating models which are simple classes and then using a mapper class that maps from the model to the database. This is explored by Matthew Weier O'Phinney in his talk Architecting Your Models, which I highly recommend looking at.
Also, for ideas on how to use the model from within the controller, the Quick Start or my tutorial are good starting points.
Are you looking for $user->find()?
Also, Zend_Db_Table != Model. You can read more about the M of MVC here:
Model Infrastructure
Zend Framework: Surviving the Deep End
Writing Robust PHP Backends with Zend Framework
Try creating a static method in your User class that returns an array of all users.