Where to put search logic with ORM models and MVC? [duplicate] - php

This question already has answers here:
Using ORM classes directly from the controller in MVC, bad practice?
(4 answers)
Closed 9 years ago.
I'm quite new to ORM's and just a bit experienced with MVC, so I was wondering the following:
I have ORM models User, Organization.. When I want to search all users in organization 1 I do the following:
$users = Model_User::query()->where('organisation_id', 1);
Should i just put that straight in the controller, or somewhere else?

Ideally you should have another layer between ORM (or data layer or persistence or repository) and controller. You can call this services/AppServices/BLL.
This layer should handle the extra logic, whereas your ORM should get data directly from database/other sources as mapped, and controller should just call next layer depending on user request.
ORM:
Users GetUsers() //for all users
Service:
Users GetUsersForOrganization(int orgId) //call orm method and filter for organization id

When i work with MVC i always have a Value Object Data1 and an mapper Data1Mapper. The ValueObject itself is for holding the data and the mapper contains methods like: find, save, delete, fetchAll etc...
In the controller i instantiate the mapper and access the needed method.
Example:
class DataMapper {
public function __construct() {
//fetch TableDateGateway
}
public function find() {
//find by identifier
}
public function save($data) {
//insert or update
}
}
class Data {
protected $_property;
public function getProperty() {
return $this->_property;
}
public function setProperty($value) {
$this->_property = $value;
}
}
class Controller {
public function indexAction() {
$id = 1;
$mapper = new DataMapper();
$data = $mapper->find($id); //--> returns if found a Data-Object
}
}

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.

Implementing business logic into models

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.

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.

Help getting Model into my Controllers, with MVC

I have been working on my own library/framework for the learning experience for a while. MVC is one of those things that took me a while to really understand but I do finally "Get it".
Below is some sample code for a basic MVC setup in PHP. I think I am in the right direction so far, where I need a little help is down in the "Example controller" near the bottom, you will see where I can create a view, I just need to figure out how to best get my data from a model file into that controller class. Please help with example code if you can, hopefully I am making sense.
Also I am welcome to any comments/suggestions on any of the code
Abstract Controller class...
/**
* MVC Example Project
*/
/**
* Extend this class with your Controllers
* Reference to the model wrapper / loader functions via $this->model
* Reference to the view functions via $this->view
*/
abstract class Core_Controller {
protected $view;
protected $model;
function __construct($dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
Abstract Model class...
/**
* Extend this class with your models and reference to the database object via $this->$db
*/
abstract class Core_Model {
protected $db;
protected $session;
function __construct($dependencyContainer) {
$this->db = $dependencyContainer->get(database);
$this->session = $dependencyContainer->get(session);
}
}
View class, might make it abstract as well...
class Core_View {
protected $data;
# Load a view file (views/$view.php);
# $param data this gets extracted and be thus be used inside the view
# When loading another view from inside the view file the data is 'cached' so you
# don't have to pass them again
public function load($view,$data = null) {
if($data) {
$this->data = $data;
extract($data);
} elseif($this->data != null) {
extract($this->data);
}
require(APP_PATH . "Views/$view.php");
}
public function set($data = null) {
if($data) {
$this->data = $data;
extract($data);
} elseif($this->data != null) {
extract($this->data);
}
}
}
Example putting it together...
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
public function profile()
{
$profileData = array();
$profileData = //GET from Model
$this->view->load('userProfile', $profileData);
}
}
?>
My suggestion is not to tie view and model to the controller at all. Let them be instantiable from controller code, just like any other classes. You can then get the model data (and pass it to the view) in standard object oriented way.
Will you use a Data access layer (DAL) / Object-relational mapping (ORM)? Take a look at Zend_Db, Doctrine or Propel
I'd say that you're missing the part of the application that manipulate your models. It could be your controller, but isn't a good practice. So we need a model mapper.
The best way to get model data from your controller is simply calling it. But generally we use a kind of "pointer" which knows how to populate your object model. This pointer is called "Mappers" (Data Mapper Pattern):
$MyModelMapper = new MyModelMapper();
$Profile = $MyModelMapper->getProfileById($id); // return Core_Model.
This function will perform a database query and will populate one specific model with the data. You could also get an array of objects for a "list" action for example.
Then you'll pass this model to your view.
I think you should take a look at the Zend Framewok quick start. It will give you some ideas.
See this question too: What's the difference between DAO and Data Mapper

Domain Object that needs more than one Data Mapper

I'm trying to figure out how to reuse Domain Models in different parts of the application and I have a feeling that the Data Mapper pattern is the way forward. The example below has methods that directly access the methods of the Mapper.
class Groups
{
protected $_groups = array();
public function addGroup($name)
{
$this->_groups[] = $name;
}
public function doSomethingGroupy($cakes)
{
// get all the groups that have cake
return $cakeyGroups;
}
}
... And a mapper to match the methods on the Groups class.
class GroupMapper
{
public function find($id, Groups $group)
{
// Mappy type things, maybe some sql
}
public function fetchByNeediness($cuddles, Groups $group)
{
// More mappy type things
}
public function save(Groups $groups)
{
// Saves
}
}
However if sometime later I wanted to use the same Groups Models but populate the groups using different queries I would use a different mapper.
class AngryGroupMapper
{
public function find($id, Groups $group)
{
// Something similar but with other tables and joins
}
public function fetchByRage($anger, Groups $group)
{
// Something new but only needed here
}
public function isEditable(Groups $groups)
{
// Do some querying
return $bool;
{
}
Now I Know the aim is Skinny Controller - Fat Model, so would I have another model to Map the Mapper (so to speak) to the Model?
class FatModelRepository
{
public function getHappyGroups()
{
$mapper = new GroupMapper();
return $mapper->fetchByNeediness('Puffy Shoes', new Groups());
}
public function getSadGroups()
{
$mapper = new AngryGroupMapper();
return $mapper->fetchByRage('Aghh!', new Groups());
{
public function save(Groups $groups)
{
$mapper = new GroupMapper();
return $mapper->save($groups);
{
}
The Data Model should have no knowledge of the Data Mapper. Your Groups class/model shouldn't have find methods and it should not have access to the mapper.
Once you remove the mapper dependency from your model your problems will go away.
NOTE: check out Doctrine 2
As rojoca says you shouldnt have the fetch/find methods directly on the model. Technically hes also right about the model not storing a reference to the mapper, but in less complex situations i think this is ok so long as the model only excpets the most abstract form of mapper you plan on having (ie. some kind of base mapper class or an interface).
Given thos to things, you should only need to add methods to the mapper, and for this i would just use inheritance, ie. extend your groups mapper for the new functionality. Of course this requires that the mapper is injectable into the model. But if youre going to have the model hold a reference to its mapper then it does need to be injectable anyhow.

Categories