Implementing business logic into models - php

I'm little confused about how do I implement some business logic inside my domain models.
I'm using php with Laravel framework, but the question is independent of the framework.
I have the following situation:
When I update a Programme, I should close all Enrollments related to that.
Generate a new Remark.
My model Programme has many Remarks and Enrollments as following:
ProgrammeController extendes Controller {
public function update($id)
{
$programme = Programme::find($id);
$programme->fill(Input::all());
$programme->update();
//Redirects .....
}
}
Programme extends Eloquent {
public function update()
{
if(!$this->valid())
return false;
$this->save();
//Should close all enrollments
$this->closeEnrollments($this->enrollments());
//Should generate a new remark
}
private function closeEnrollments($enrollments)
{
foreach($enrollments as $enrollment)
{
$enrollment->close();
}
}
public function enrollments()
{
return $this->hasMany('Enrollment');
}
public function remarks()
{
return $this->hasMany('Remark');
}
}
Enrollment extends Eloquent {
public function programme()
{
return $this->belongsTo('Programme');
}
public function close()
{
//do something
}
}
Remark extends Eloquent {
public function programme()
{
return $this->belongsTo('Programme');
}
public function generate()
{
//do something
}
}
My controller calls the function update() in the model Programme.
How could I implement this situation so that my Models can be testable and following the correct patterns? Because I don't know what is the best way to handle it and to call the function generate() in the class Remark and close() in the class Enrollments.
Many thanks!

In PHPUnit you can mock protected methods or create of subclass that would expose private methods like the closeEnrollments() and thus allows you create stubs for testing - see http://phpunit.de/manual/3.7/en/test-doubles.html for more.
However I would suggest you to improve your architecture and look into concepts like TDD, SOLID, Onion Architecture etc. Those are just the core concepts for higher concepts like DDD (and its components like Value object, Entity, Repository, Service and so on). Eg. in your example the class Programme provides persistence, validation, some other business logic and is an aggregate root at the same time. Class that does too much is hard to test. Also such class is hard to change in future when business requirements change and so on.

Related

Trying to abstract the Database class and stop it's public usage : Laravel 5.2.31

I have following classes
Interface
interface IRole {
public function All();
}
In below class, I will also write some logic before sending the data to Database layer and some code after data is retrieved from Database class
class RoleBL implements IRole {
public function All() {
return (new RoleDb())->All();
}
}
Database class
class RoleDb {
public function All() {
$Roles = \App\Models\Role\RoleModel
::all();
return $Roles;
}
}
Below is my Controller Action Method
class MembershipController extends \App\Http\Controllers\BaseController
{
private $role;
public function __construct(IRole $_role) {
$this->role = $_role;
parent::__construct();
}
public function create() {
$RoleTypes = $this->role->All();
return view('Membership.Create', array('Roles' => $RoleTypes));
}
}
Can somebody help me how can I stop direct access to RoleDb class? It's access modifier is public right now.
You should not even try to do that. At PHP it will look more like hacks. Just keep one code style and if you work in team then write some guidelines.
Keep all your objects in separate layers based on their purpose. Database related objects in data access layer. Domain objects in domain layer.
Models at domain layer represent all business objects that business talk about (role, customer, comment, payment, etc.) and all related actions on them (user->register, user->assignRole, blog->postArticle, etc.). These models hold business requirements, rules.
Models at data access layer represents all objects that persists state of those business objects and finds them in specific ways (blog->getUnreadPosts, user->findAdministrators, etc.).
If by RoleBL you mean role business logic then it should be part of domain (your business requirements). These objects are persisted/retrieved by DAL.
Your business objects should not know anything about data access layer. (new RoleDb())->All(); means reading from database. To fix this you can create separate read model at DAL for querying your business objects. That means there will be separate model (e.g. RoleDataModel) at DAL for designing query side based on business/application needs.
namespace Domain
{
class Role
{
private $id;
private $title;
/* etc. */
public function getId()
{
return $this->id;
}
}
class Person
{
private $roles = [];
/* etc. */
public function assignRole(Role $role)
{
// person already promoted?
// can person be promoted ?
// if you promote person then it might not be required to add instance af this $role
// to $this->roles array, just add identifier from this role
// If you would like to respond on what did just happen
// at this point you can return events that describe just that
}
public function getRoles()
{
return $this->roles;
}
}
}
namespace DAL
{
class Person
{
function storePerson(Person $person)
{
// here you can use eloqueent for database actions
}
function getAllAdministrators()
{
}
}
}
There will be separate Person class for eloquent. Use that just for data manipulation. Map data from eloquent objets to Data Transfet Objects or your Business Layer Objects. DTOs can be more specific to your other layers like UI where you might not need to show everything that BLOs contains. DTOs for your UI will model everything your UI needs.
Get familiar with some of the DDD and overall programming concepts. I should be able to look up something that will fit your needs.

Using the Repository Pattern (and Query Scopes) with Relations

In Laravel 4, query scopes are available on all queries (including ones generated by relations queries). This means that for the following (example) models:
Customer.php:
<?php
class Customer extends Eloquent {
public function order() { return $this->hasMany('Order'); }
}
Order.php:
<?php
class Order extends Eloquent {
public function scopeDelivered($query) { return $query->where('delivered', '=', true); }
public function customer() { return $this->belongsTo('Customer'); }
}
Both of the following work:
var_dump(Order::delivered()->get()); // All delivered orders
var_dump(Customer::find(1)->orders()->delivered()->get()); // only orders by customer #1 that are delivered
This is useful from within a controller because the query logic for finding delivered orders doesn't have to be repeated.
Recently, though, I've been convinced that the Repository pattern is optimal for not only separation of concerns but also for the possibility of a ORM/DB switch or the necessity of adding middleware like a cache. Repositories feel very natural, because now instead of having scopes bloat my models, the associated queries are instead part of the Repository (which makes more sense because naturally this would be a method of the collection not the item).
For example,
<?php
class EloquentOrderRepository {
protected $order;
public function __construct(Order $order) { $this->order = $order; }
public function find($id) { /* ... */ }
/* etc... */
public function allDelievered() { return $this->order->where('delivered', '=', true)->get(); }
}
However, now I have the delivered scope repeated, so to avoid violating DRY, I remove it from the model (which seems logical as per the justification above). But now, I can no longer can use scopes on relations (like $customer->orders()->delivered()). The only workaround here I see is somehow instantiating the Repository with the pre-made query (similar to what is passed to the scopes in the models) in the Relation base class. But this involves changing (and overriding) a lot of code and default behavior and seems to make things more coupled than they should be.
Given this dilemma, is this is misuse of a repository? If not, is my solution the only way to regain the functionality that I would like? Or is having the scopes in the models not tight enough coupling to justify this extra code? If the scopes aren't tight coupling, then is there a way to use both the Repository pattern and scopes while still being DRY?
Note: I am aware of some similar questions on similar topics but none of them address the issue presented here with queries generated by relationships, which do not rely on the Repository.
I've managed to find a solution. It's rather hacky and I'm not sure whether I consider it acceptable (it uses a lot of things in ways that they likely weren't meant to be used). To summarize, the solution allows you to move scopes to the repository. Each repository (on instantiation) is booted once, and during this process all of the scope methods are extracted and added to each query created by the eloquent model (via macros) by way of a Illuminate\Database\Eloquent\ScopeInterface.
The (Hack-y) solution
Repository Pattern Implementation
app/lib/PhpMyCoder/Repository/Repository.php:
<?php namespace PhpMyCoder\Repository;
interface Repository {
public function all();
public function find($id);
}
app/lib/PhpMyCoder/Repository/Order/OrderRepository.php:
<?php namespace PhpMyCoder\Repository\Order;
interface OrderRepository extends PhpMyCoder\Repository\Repository {}
Adding Eloquent Repositories (and a hack)
app/lib/PhpMyCoder/Repository/Order/EloquentOrderRepository.php:
<?php namespace PhpMyCoder\Repository\Order;
use PhpMyCoder\Repository\EloquentBaseRepository;
class EloquentOrderRepository extends EloquentBaseRepository implements OrderRepository {
public function __construct(\Order $model) {
parent::__construct($model);
}
public function finished() {
return $this->model->finished()->get();
}
public function scopeFinished($query) {
return $query->where('finished', '=', true);
}
}
Notice how the repository contains the scope that would normally be stored in the Order model class. In the database (for this example), Order needs to have a boolean column finished. We'll cover the details of EloquentBaseRepository below.
app/lib/PhpMyCoder/Repository/EloquentBaseRepository.php:
<?php namespace PhpMyCoder\Repository;
use Illuminate\Database\Eloquent\Model;
abstract class EloquentBaseRepository implements Repository {
protected $model;
// Stores which repositories have already been booted
protected static $booted = array();
public function __construct(Model $model) {
$this->model = $model;
$this->bootIfNotBooted();
}
protected function bootIfNotBooted() {
// Boot once per repository class, because we only need to
// add the scopes to the model once
if(!isset(static::$booted[get_class($this)])) {
static::$booted[get_class($this)] = true;
$this->boot();
}
}
protected function boot() {
$modelScope = new ModelScope(); // covered below
$selfReflection = new \ReflectionObject($this);
foreach (get_class_methods($this) as $method) {
// Find all scope methods in the repository class
if (preg_match('/^scope(.+)$/', $method, $matches)) {
$scopeName = lcfirst($matches[1]);
// Get a closure for the scope method
$scopeMethod = $selfReflection->getMethod($method)->getClosure($this)->bindTo(null);
$modelScope->addScope($scopeName, $scopeMethod);
}
}
// Attach our special ModelScope to the Model class
call_user_func([get_class($this->model), 'addGlobalScope'], $modelScope);
}
public function __call($method, $arguments) {
// Handle calls to scopes on the repository similarly to
// how they are handled on Eloquent models
if(method_exists($this, 'scope' . ucfirst($method))) {
return call_user_func_array([$this->model, $method], $arguments)->get();
}
}
/* From PhpMyCoder\Repository\Order\OrderRepository (inherited from PhpMyCoder\Repository\Repository) */
public function all() {
return $this->model->all();
}
public function find($id) {
return $this->model->find($id);
}
}
Each time an instance of a repository class is instantiated for the first time, we boot the repository. This involves aggregating all "scope" methods on the repository into a ModelScope object and then applying that to the model. The ModelScope will apply our scopes to each query created by the model (as seen below).
app/lib/PhpMyCoder/Repository/ModelScope.php:
<?php namespace PhpMyCoder\Repository;
use Illuminate\Database\Eloquent\ScopeInterface;
use Illuminate\Database\Eloquent\Builder;
class ModelScope implements ScopeInterface {
protected $scopes = array(); // scopes we need to apply to each query
public function apply(Builder $builder) {
foreach($this->scopes as $name => $scope) {
// Add scope to the builder as a macro (hack-y)
// this mimics the behavior and return value of Builder::callScope()
$builder->macro($name, function() use($builder, $scope) {
$arguments = func_get_args();
array_unshift($arguments, $builder->getQuery());
return call_user_func_array($scope, $arguments) ?: $builder->getQuery();
});
}
}
public function remove(Builder $builder) {
// Removing is not really possible (no Builder::removeMacro),
// so we'll just overwrite the method with one that throws a
// BadMethodCallException
foreach($this->scopes as $name => $scope) {
$builder->macro($name, function() use($name) {
$className = get_class($this);
throw new \BadMethodCallException("Call to undefined method {$className}::{$name}()");
});
}
}
public function addScope($name, \Closure $scope) {
$this->scopes[$name] = $scope;
}
}
The ServiceProvider and Composer File
app/lib/PhpMyCoder/Repository/RepositoryServiceProvider.php:
<?php namespace PhpMyCoder\Repository;
use Illuminate\Support\ServiceProvider;
use PhpMyCoder\Repository\Order\EloquentOrderRepository;
class RepositoryServiceProvider extends ServiceProvider {
public function register() {
// Bind the repository interface to the eloquent repository class
$this->app->bind('PhpMyCoder\Repository\Order\OrderRepository', function() {
return new EloquentOrderRepository(new \Order);
});
}
}
Be sure to add this service provider to the providers array in the app.php config:
'PhpMyCoder\Repository\RepositoryServiceProvider',
And then add the app/lib to composer's autoload
"autoload": {
"psr-0": {
"PhpMyCoder\\": "app/lib"
},
/* etc... */
},
This will require a composer.phar dump-autoload.
The Models
app/models/Customer.php:
<?php
class Customer extends Eloquent {
public function orders() {
return $this->hasMany('Order');
}
}
Notice that for brevity, I've excluded writing a repository for Customer, but in a real application you should.
app/model/Order.php:
<?php
class Order extends Eloquent {
public function customer() {
return $this->belongsTo('Customer');
}
}
Notice how the scope is not longer stored in the Order model. This makes more structural sense, because the collection level (repository) should be responsible for scopes applying to all orders while Order should only be concerned with details specific to one order. For this demo to work, order must have an integer foreign key customer_id to customers.id and a boolean flag finished.
Usage in the Controller
app/controllers/OrderController.php:
<?php
// IoC will handle passing our controller the proper instance
use PhpMyCoder\Repository\Order\OrderRepository;
class OrderController extends BaseController {
protected $orderRepository;
public function __construct(OrderRepository $orderRepository) {
$this->orderRepository = $orderRepository;
}
public function test() {
$allOrders = $this->orderRepository->all();
// Our repository can handle scope calls similarly to how
// Eloquent models handle them
$finishedOrders = $this->orderRepository->finished();
// If we had made one, we would instead use a customer repository
// Notice though how the relation query also has order scopes
$finishedOrdersForCustomer = Customer::find(1)->orders()->finished();
}
}
Our repository not only contains the scopes for the child model, which is more SOLID. They also come with the ability to handle calls to the scope like a real Eloquent model would. And they add all scopes to each query created by the model so that you have access to them when retrieving related models.
Problems with this Approach
A lot of code for little functionality: arguably too much to accomplish the desired result
It's hacky: macros on Illuminate\Database\Eloquent\Builder and Illuminate\Database\Eloquent\ScopeInterface (in conjunction with Illuminate\Database\Eloquent\Model::addGlobalScope) are likely used in ways they weren't intended to be
It requires instantiation of the repository (MAJOR ISSUE): if you're within the CustomerController and you only have instantiated CustomerRepository, $this->customerRepository->find(1)->orders()->finished()->get() won't work as expected (the finished() macro/scope won't be added to each Order query unless you instantiate OrderRepository).
I'll investigate if there is a more elegant solution (which remedies the issues listed above), but this is the best solution I can find thus far.
Related Resources on the Repository Pattern
Creating flexible Controllers in Laravel 4 using Repositories
Eloquent tricks for better Repositories

How to inject multiple classes that share the same interface in Laravel 4

Say I have an interface CrawlerInterface with implementation PageCrawler and FeedCrawler; if we happen to need both classes in a controller, how can that be achieved with constructor injection?
Previously we use a central ServiceProvider to register (i.e. App::bind) such classes, but in most cases we only have 1 implementation of an interface, so said problem hasn't occured to us yet.
PS: I also wonder if this problem suggests we should split the controller.
Updates:
Thanks for the comments and response, to explain, said interface has only one public method: crawl($uri), and both page/feed crawler implements it as given a resource identifier, return resource.
My follow up question:
Say we are in a calculator scenario where Addition, Subtraction and Multiplication share the same interface Operation, which has only 1 public method run, at some point we will still encounter this problem right? How do we handle situation like these in general with ServiceProvider?
If each crawler exists for a different reason, you can use arbitrary names for your instances, for example:
App::bind('crawler.allArticles', 'PageCrawler');
App::bind('crawler.latestArticles', 'FeedCrawler');
For the controller:
App::bind('CrawlerController', function($app) {
return new CrawlerController(
App::make('crawler.allArticles'),
App::make('crawler.latestArticles')
);
});
Your controller code would then use each crawler differently:
public function showLatestArticlesAction()
$latestArticles = $this->latestArticlesCrawler->crawl();
// ...
}
public function showAllArticlesAction()
$allArticles = $this->allArticlesCrawler->crawl();
// ...
}
If you just have a list of crawlers where each is used for the same thing, you probably want to do something like:
App::bind('crawlers', function($app) {
return [
App::make('PageCrawler'),
App::make('FeedCrawler'),
];
});
In your controller, you'll get a list of "crawlers" by configuring it like so:
App::bind('CrawlerController', function($app) {
return new CrawlerController(App::make('crawlers'));
});
Your controller code could be something like this:
public function showArticlesAction()
$allArticles = array();
foreach ($this->crawlers as $crawler) {
$allArticles = array_merge($allArticles, $this->crawler->crawl());
}
// ...
}
Ok lets assume you have a CrawlerController
class CrawlerController extends BaseController
{
protected $crawler1;
protected $crawler2;
public function __construct(CrawlerInterface $c1, CrawlerInterface $c2)
{
$this->crawler1 = $c1;
$this->crawler2 = $c2;
}
}
an interface
interface CrawlerInterface{}
and concrete implementations of that intefrace called PageCrawler and FeedCrawler
class PageCrawler implements CrawlerInterface{}
class FeedCrawler implements CrawlerInterface{}
You would inject the dependencies by writing a service locator like
App::bind('CrawlerController', function($app) {
$controller = new CrawlerController(
new PageCrawler,
new FeedCrawler
);
return $controller;
});
But as suggested by others you should rethink your logic, use it only if this kind
of architecture is unavoidable
I think that the interface won't help you in this case.
By doing:
App::bind('CrawlerInterface', '<implementation>');
You need to choose one:
App::bind('CrawlerInterface', 'PageCrawler');
or
App::bind('CrawlerInterface', 'FeedCrawler');
And then Laravel will inject it:
class CrawlerController {
public function __construct(CrawlerInterface $crawler)
{
}
}
To have both you have 2 options
-Have 2 different interfaces
-Inject the implementations directly:
class CrawlerController {
public function __construct(PageCrawler $pageCrawler, FeedCrawler $feedCrawler)
{
}
}
But I also think that, if you need something like this, you better rethink your logic.

Zend Framework 2 - Best way to call mapper functions in Models

I preferred double layer models (mapper and model) over doctrine in my zend framework 2 project and trying to make them work little bit like doctrine so I can access relational data from the models (entities). Following example demonstrates what I am trying to achieve.
class User
{
protected $userTable;
public $id;
public $name;
public function __construct($userTable)
{
$this->userTable = $userTable
}
public function getUserArticles()
{
return $this->userTable->getUserArticles($this->id);
}
}
Problem is I cannot inject my user table in my user model, because table gateway uses model class as array object prototype which gets later injected to create user table gateway (mapper).
I don't want to inject service manager in my models as it is considered as a bad practice. How can I inject my user table in my user model? is it possible? what is the best way to achieve what I am trying to do
What you are trying to do is mix two design patterns: Active Record and Data Mapper.
If you take a look at the Data Mapper pattern, you have the Mapper that accesses both the Model and the database. The Model is passive - usually does not call external resources (it's a POPO - Plain Old PHP Object).
A solution for your issue is to inject the related information into the Model, thus keeping the Model only as a data structure.
Here is a working scenario for an MVC application:
Controller - used for input validation & retrieving data from services
<?php
...
public function viewAction()
{
$id = (int) $this->params()->fromQuery('id');
$service = $this->getServiceLocator()->get('your-user-service-name');
$user = $service->getUser($id);
...
}
Service - used for executing the business logic; calls multiple Data Mappers
<?php
...
public function getUser($id)
{
// get user
$mapper = $this->getServiceLocator()->get('your-user-mapper');
$user = $mapper->getUserById($id);
// get articles
$article_mapper = $this->getServiceLocator()->get('your-article-mapper');
$user->articles = $article_mapper->getArticlesByUser($id);
return $user;
}
Data Mapper - used to manipulate one type of Domain entity - it should be composed with a tableGateway if you are accessing the database
<?php
...
public function getUserById($id)
{
$select = $this->tableGateway->getSql()->select();
$select = $select->where(array('id' => $value));
$row = $this->tableGateway->selectWith($select)->current();
return $row;
}
Domain Model - used for data representation
<?php
...
class User
{
public $name; // user name
...
public $articles; // holds the user articles
}
Advantages
Passive Models are easy to read - understand the data structure and it's relations.
Passive Models are easy to test - you don't need external dependencies.
You separate the persistence layer from the Domain layer.
Hope this helps!
You should not inject your mapper into your model, that's exactly the other way around. Important for you to understand is the way the relations work and models shouldn't have any knowledge how their data is mapped to a persistency framework.
You refer to Doctrine, so I'd suggest you also look at how Doctrine solves this problem. The way they do it is via a Proxy. A proxy is a generated class (you need to write your own generator or write all proxies yourself) which extends the model and have the mapper injected:
class Foo
{
protected $id;
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
class ProxyFoo extends Foo
{
protected $mapper;
public function __construct(FooMapper $mapper)
{
$this->mapper = $mapper;
}
public function getName()
{
if (null === $this->name) {
$this->load();
}
return parent::getName();
}
protected function load()
{
$data = $this->mapper->findById($this->id);
// Populate this model with $data
}
}
My suggestion: look either at the default mapper pattern Zend Framework 2 applies and forget lazy loading, or just use Doctrine. This is too much work to get this done properly.

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.

Categories