I'm in the process of learning OOPHP, and I'm looking for some 'best practice' advice.
With a relational db, there's obviously foreign keys in many tables. When I am creating Models for my site I am trying to determine if it is better to do JOIN queries in the model, or have one model call another?
Calling other models seems to keep the code more modular. Either way seem to create dependencies, whether it be on another table or anther model.
If I go with the 'call other models' approach I seem to run into another problem: infinite loops. Here's the example I'm running into. I have 2 tables, person and school. Each person has a favorite school, represented by a schoolId. Each school has a principal, that is a personId.
The person object the row is mapped to accepts a school object in its constructor, but the school object the school row is mapped to accepts a person object in its constructor.
From what I've found, something about lazy loading, seems to be the solution, but (I could be wrong) it seems if I do that I can't use PHP's type hinting.
(I'm guessing many will suggest an ORM tool like Doctrine to me, and it's something I will definitely look into in the future. I am avoiding it now because of its supposed steep learning curve, and because I feel I understand those tools better later on if I try it myself once)
Nice question! I had/have same thoughts. And I think it's a really good idea to start code your own before looking at frameworks :)
When I coded my model generator I decided once not to use JOINS and use 'lazy loading'. Meaning I have classes like this (just pseudo code):
class Person extends Model_Resource {
/**
*
*/
protected $name;
/**
* #var Address <-- external reference
*/
protected $address;
/**
* #return Person
*/
public static function select($filter) {
return DB::instance(filter_query('SELECT * FROM `person`', $filter));
}
/**
* #return Address
*/
public function getAddress() {
// lazy loading :
if(is_null($this->address)) {
$this->address = Address::select(new Filter('p_id', $this->id));
}
return $this->address;
}
}
That worked good for me in a medium size application over years. When I really need to speed up things or using a somewhat inregular query, then I'm free to overwrite the auto generated methods and use JOINS, UNION etc...
I'm curious what will others say.
Related
I'm starting a little project with DDD approach. I've created my domain model with Entities and ValueObjects in plain PHP. Entities have references to their associations - in my case, there is an Employee entity with collection of Teams he belongs to, and I keep them in Employee::teams property. Everything is going great, I've created mappings for those entities with associations in YAML, interfaces for repositories to be implemented in the Symfony2 and Doctrine2 layer, etc.
When I fetch Employees from repository (with Doctrine's EntityManager::findAll()) instead of array of Teams I receive PersistentCollection with those teams. It's built on PHP7, and Employee::getTeams() has return type of array so I'm getting the critical exception.
Is there any way to convert it into array with some external listeners (Symfony layer only, to not to mess in domain files) or any other core mechanism?
Thanks.
You get ArrayCollection
http://www.doctrine-project.org/api/common/2.5/class-Doctrine.Common.Collections.ArrayCollection.html
It has toArray() method so you can write
/**
* #return Team[]
*/
public function getTeams(): array
{
return $this->teams->toArray();
}
But I prefer
/**
* #return Team[]|ArrayCollection
*/
public function getTeams()
{
return $this->teams;
}
If you use good IDE like PhpStorm it will understand it correctly to autocomplete it like ArrayCollection and as Team if you make foreach ($employee->getTeams() as $team) {...
ArrayCollection are more powerful than plain arrays. For example you can filter and order them and Doctrine will make optimized SQL so you don't have to load all items
http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#filtering-collections
Kuba,
That doesn't seem to be a DDD question but a technical question regarding PHP implementation details.
However, please let me jump into your design and try to find a better solution for your domain. You are missing to model the relation between an employee and a team, let's call it employee in team. The way you did you are forcing an AR to reference another AR, saying this way that employee manages a team life cycle. This might not be the case but under a specific scenario another actor might change a status of a team and that could break the Employee invariant over the Team AR. Because of this when you make an AR to manage another AR life cycle then this dependency shouldn't be found through the repository and the root of the aggregate is in the position to now force invariant around the aggregated AR.
There are still some concerns to mention, but just to keep it simple, go ahead and model the concept you are missing: EmployeeInTeam or whatever you want to call it. It references the conceptual identity of the employee and the team so you can remove it safely in a future, you can query employees in a team and teams an employee is part of.
If you need frameworks or the DB to keep the consistence then you are not doing DDD. Use just objects, not the technology.
Regards,
Sebastian.
As a means to try and learn object oriented PHP scripting, I'm currently attempting to rewrite a database web application that I previously wrote in procedural PHP. The application is used to store car parts and information about car parts.
In my application there are car parts. They are identified by various reference numbers, which are assigned by different organisations (the part's manufacturer, re-manufacturers, vehicle manufacturers, etc.), and any particular car part could have zero, one or many reference numbers as assigned by these organisations (and, confusingly, each reference number may refer to more than one unique car part as defined in the database I'm working on).
As far as I understand things, I am dealing with three different classes of entities. There is that of the car part, the reference number, and that of the reference-assigner (in my internal nomenclature I call these 'referrers'). As I am just getting started with learning OOP, I have begun by creating some very basic classes for each:
class Part {
public $part_id;
public $part_type;
public $weight;
public $notes;
public $references;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
}
class Reference {
public $reference_id;
public $reference;
public $referrer;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
}
class Referrer {
public $referrer_id;
public $referrer_name;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
}
?>
What I've been struggling with is how to populate these and subsequently glue them together. The most basic function of my web application is to view a car part, including its metrics and its assigned reference numbers.
A part_id is included in a page request. Initially, I wrote a constructor method in my Part class which would look up that part in the database, and then another method which would look up reference numbers (which were JOINed with the referrer table) assigned to that Part ID. It would iterate through the results and create a new object for each reference, and hold the complete set in an array indexed by the reference_id datum.
After further reading, I began to understand that I should use a factory class to carry this out, however, as this kind of conjunction between my Part class and my Reference class is not the responsibility of any one of those discrete classes. This made conceptual sense, and so I have since devised a class that I've called PartReferenceFactory, which I understand should be responsible for assembling any kind of collation of part reference numbers:
class PartReferenceFactory {
public static function getReferences(Database $db, $part_id) {
$db_result = $db->query(
'SELECT *
FROM `' . REFERRER_TABLE . '`
LEFT JOIN `' . REFERENCE_TABLE . '` USING (`referrer_id`)
INNER JOIN `' . REFERENCE_REL . '` USING (`reference_id`)
WHERE `part_id` = :part_id
ORDER BY `order` ASC, `referrer_name` ASC, `reference` ASC',
array(':part_id' => $part_id);
);
if(empty($db_result)) {
return FALSE;
} else {
$references = array();
foreach($db_result as $reference_id => $reference_properties) {
$references[$reference_id] = new Reference($db);
$references[$reference_id]->reference = $reference_properties['reference'];
$references[$reference_id]->referrer = new Referrer($db);
$references[$reference_id]->referrer->referrer_id = $reference_properties['referrer_id'];
$references[$reference_id]->referrer->referrer_name = $reference_properties['referrer_id'];
}
return $references;
}
}
}
?>
The getReferences method of my factory class is then called inside my Part class, which I revised thusly:
class Part {
public $part_id;
public $part_type;
public $weight;
public $notes;
public $references;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
function getReferences() {
$this->references = PartReferenceFactory::getReferences($this->db, $this->part_id);
}
}
Really I'm looking for advice on whether my general approach is a good one or if, as I suspect, I've misunderstood something, am overlooking other things, and am tying myself in knots. I will try to distill this into some underlying, directly-answerable questions:
Does my understanding of the purpose of classes and of factory classes seem erroneous?
Is it a bad idea for me to store arrays of objects? I ask from a viewpoint more of design than performance, insofar as they are not intertwined.
Is this even an appropriate way to structure relationships/inter-dependencies between classes in PHP?
Is it correct to call my PartReferenceFactory inside the getReferences method of the (instantiated) Part class? And is storing the returned reference objects within the part objects appropriate?
As a part of the application's GUI, I'll need to provide lists of referrers, requiring me to create another array of ALL referrers independently of any part object. Yet some of these referrers will exist within the $references array inside the part object. It has occurred to me that in order to avoid duplicate referrer objects, I could SELECT a list of all referrers at the beginning of each page request, formulate these into a global array of referrers, and simply reference these from within my Part and Reference classes as needed. However, I have read that it is not good to rely on the global scope within classes. Is there an elegant/best-practice solution to this?
Thank you very much for the time of whomever happens to read this, and I apologise for the extremely long question. I often worry that I mis-explain matters, and so wanted to be as precise as possible. Please let me know if it would be beneficial for any parts of my post to be deleted.
Hmmm, I think Jon is right, this question has taken real commitment to get my head round and I'm still not sure I know everything you're asking, but I'll give it a go.
1) Is your understanding of classes/factory classes right?
Well yes and no. Yes you do understand it, but you're taking a very classroom type approach to the problem, rather than the sort of pragmatism that comes with experience. OOP is a way of modelling real things, but it's still fundamentally a programming tool, so don't over complicate your structure, just to keep it real. The main think OOP gives you over structured programming is inheritance, which means you can code things which are kind of like other things, so reuse code better by only coding the stuff that's actually different. If 2 things would share code, make them a shared parent and put the code in there. Envisaging how the hierarchy might work efficiently is 80% of the task of designing an OOP application, and you're bound to get it wrong first time and have to restructure.
For example. I've coded a number of classes which represent entities in a database: ie Users, Realthings, Collectibles. Each has fundamentally different set of attributes and relations, but also has a core set of things that are similar: the way they're interrogated, the way the data is presented, the fact that they have attributes, fields and relations. So I coded a parent class which everything inherits, and it contains most of the code, then the specific children just define the stuff which is different for each class, and the hard work was deciding how much could go in Indexed to avoid repeating code in the child classes. Initially I had a lot of code in the children, but as it evolved I moved more and more code into the parent class, and thinned out the children. Like I say, don't assume you'll get it right first time.
2) Is it a bad idea to store objects in arrays? Well no, but why would you want to? In your example you have an array of references in the database, where the relationship is a many to many relation (ie you have 3 tables with a joining table in the middle). Then when you get the data you create each object and store it in an array. Sure you can do that, but remember each object has an overhead. Loading everything into memory is fine, but don't do it unless you need to. Also be sure you need a many<=>many relationship. I may have mis-read your explanation, but I thought you'd only need a one<=>many relationship, which could be done by storing a reference to the 'one' in the record of each of the 'many' (thus loosing the middle table and simplifying the join).
5) I'm going to jump to the last one at this point because it feeds into #2. You need to think of your application from different perspectives (a) when you have data and are trying to present it (read) (b) when you have some data and are trying to add new data, with links to existing data (adding a relation) and (c) when you have no data, and are adding a fresh record. How might the user get to each of those perspectives and what would happen. It's often easy to get (a) and (c) to work, but making (b) intuitive is often the hard part, and if you over complicate it, then users simply won't get it. When building up the inner data structures in #2 only do what you need, for the perspective a user is in. Unless you're writing an app, you don't need to load everything, only the stuff for the task at hand.
3) Not sure what you mean by 'is this appropriate'. OOP allows you to tie yourself up in knots, or create wonderful works of art. In theory you should just try to keep it simple. There are great books which give examples of why you might want to make things more complicated, but most of the reasons aren't obvious, so you'll need experience to decide if what you're doing is needlessly over complicating things or if it actually avoids a pitfall. From personal experience if you think it's overcomplicating, it probably is.
4) Not sure if this is what you meant, but I've taken this question to be asking if you've used Factory classes correctly. My understanding of the theory is that what you should end up with, is next to no references to doing things statically, beyond the initial creation of a FactoryClass object, stored statically in the child class being used, then you have to code an abstract method in parent class, implemented in each child class, which gets you that object, so you can call on the object of that FactoryClass, using a method call. I normally only call FactoryClass stuff directly when a non-factory class is initialised (ie you need to populate the static in a new object, incase the class hasn't been init'd yet). I don't see any problem calling it directly as you have, but I'd avoid that as the Factory is IMHO an implementation detail of the class, and so shouldn't be exposed outside that.
In my experience you're always learning new things and discovering pitfalls in OOP, so even thought I've been coding professionally for nearly 20 years, I'd not claim to be an expert on this, so I'm sure there will be totally opposing views to what I've said. I think you learn most by doing what your gut says is right, then not being too stubborn to start over again, if you gut changes it's mind :-)
Let's say I have two models:
class Book
{
public $book_id;
public $book_author_id;
public $title;
}
class Author
{
public $author_id;
public $author_name;
}
I'm used to writing something like this:
$DB->query("SELECT book_id, title, author_name
FROM book
LEFT JOIN author
ON book_author_id = author_id
");
Let's assume, that I'm not interested in having separate queries for this association. How do I proceed? Here are some things I've heard:
create a MySQL VIEW of the JOIN
create a model class of the VIEW
The application I'm working on involves dozens of tables and was highly optimized in procedural code (almost no SELECT * anywhere, for instance). I'm refactoring to make it more maintainable (I'm the original creator, too), but I would like to have the flexibility of using joins when I need to without compromising the structure of my files and DB calls.
A possibly related question I have is related to including other models:
class Author
{
public $author_id;
public $author_name;
/* #var Book */ //<--don't really fully understand this but I've seen something like it somewhere
public (array) $authors_books;
}
I'm still searching for answers, but if you could send a link my way, that would be appreciated.
What you are calling "models" are actually domain objects. They should be responsible for dealing with domain business logic, and have nothing to do with storage.
The storage-related logic and interaction should be handled by separate group of objects. One of most sensible solution is using data mappers. Each mapper can deal with multiple tables and complicated SQL.
As for your query, the result of such query would contain information, appropriate for passing to a collection of domain objects.
BTW, that query is quite useless. You forget that each book can have multiple authors. Take for an example this book - it has 4 different authors. To make this query useful, you should have to do GROUP_CONCAT() based on either author_id or book_id.
When implementing such JOIN statements, the database response most likely will be a collection:
$mapper = $factory->buildMapper('BookCollection');
$collection = $factory->buildCollection('Book');
$collection->setSomeCondition('foobar');
$mapper->fetch( $collection );
foreach ( $collection as $item )
{
$item->setSomething('marker');
}
$mapper->store( $collection );
P.S. Your code example seem to be leaking abstraction. It is a bad practice to let other structures to access object's variables directly.
P.P.S. It seems that your understanding of model part of MVC is quite different from how i see it.
Database joins are an artefact of relational databases, you don't need to model them. You need to model what your data is and how it behaves, e.g. you might have a getBooks() method in your Author instances, or a static getByAuthor() method in your Book class (generally, $author->getBooks() should be implemented as Book::getByAuthor($this), so your Author class shouldn't be concerned with Book's implementation details). It's not always a good idea to automatically instantiate all related data (e.g. instantiate Book instances for all books by a given Author instance, as you seem to be considering with your $author_books property), since this might easily degrade into a "load the entire database to memory for each request" scenario.
If you're trying to model your DB in classes, this is a problem that's already been solved.
I suggest you try out the Doctrine framework, which is a full ORM framework for PHP.
Hope that helps.
I need to figure out a best practice for utilizing models efficiently in Zend Framework.
Currently, I have classes extending Zend_Db_Table_Abstract which handle my queries to each class' respective table.
When I need to access say 5 of those tables from a controller, I find myself creating 5 new instances of each specific Zend_Db_Table object. This is really ineffective.
I've thought about implementing a Factory pattern to create new instances (or provide existing static copy) but am not sure. Is this the best way to go about it?
What is the correct way to handle models ensuring speed without consuming excessive resources? Should lazy loading come into play here?
[EDIT]
As an example, I have a class I use to handle getting details about a location from a raw search query and need these objects in order to parse the query:
// Initialize database object
$this->dbLocations = new Model_Locations;
$this->dbStates = new Model_States;
$this->dbZipcodes = new Model_Zipcodes;
$this->dbLookup = new Model_Lookup;
In another class, I may need to access those models again so I repeat the above code. Essentially reinitializing objects that could be static/singleton.
I tend to work at the DbTable like you do. I've found it effective when I need to query multiple tables in a single action to create another layer of model above the dbTable. Similar to a service or domain layer. This way I only have to call a single model but I still have the functionality I need.
Here is a simple example that may eventually interact with 5 DbTable classes and most likely a couple of Row classes as well:
<?php
class Application_Model_TrackInfo
{
protected $_track;
protected $_bidLocation;
protected $_weekend;
protected $_shift;
protected $_station;
public function __construct() {
//assign DbTable models to properties for convience
$this->_track = new Application_Model_DbTable_Track();
}
/**
*
* #param type $trackId
* #return type object
*/
public function getByTrackId($trackId) {
$trackData = $this->_track->fetchRow($trackId);
//getAllInfo() Application_Model_Row_TRack
$result = $trackData->getAllInfo();
//returns std object reflecting data from 3 DbTable classes
return $result;
}
/**
*Get Station from trackid through bidlocationid
*
* #param type $trackId
* #return type object
*/
public function getStation($trackId){
$data = $this->_track->fetchRow($trackId);
//This a Application_Model_Row_Track method
$result= $data->getStationFromBidLocation();
return $result;
}
}
I hope this helps.
[EDIT]
Since I wrote this answer I have learned the benefits of Domain Models and Data Mappers. Wow what a difference in my app. Not a magic bullet, but a huge improvement.
Thanks to
Alejandro Gervasio over at PHPMaster.com
Rob Allen at Akrabat.com
and
Pádraic Brady at Surviving The Deepend
for all their help in understanding this pattern.
You seem to be in a possition where you require efficient data management with features that the current Zend framework does not come with. Zend does not have a built in engine for working with databases of any sort, it simply has a wrapper classes which help you write your queries.
What you need is an object relational model (ORM) which is a must-have in a professional framework. As I understand it, ORM is a framework by itself, it has patterns and strongly defined ways of "doing things", supports lazy loading (it takes the most of it) and optimizes your queries to the fullest. When you use ORM you don't even write SQL, instead you need to change your interpretation of data storage, you need to forget about tables and focus on Objects. In Doctrine for example each type (table) is specified by a Class and each record (row) as a class instance where you have access to different methods and properties. It supports event listeners and crazy cascading rellations.
No more need to extract rows from related tables when you delete records (it is automatic), no more need to write complex and chaotic scripts to ensure filesystem synchronisation, you can migrate to almost any db engine at any time (mysql, postgresql, simplesql..) and more..
I've been using Doctrine 2 in conjunction with Symfony 2 framework and I have to say I wouldn't go back to Zend for anything. Yes, it is complex and heavy, but really the ultimate solution. When you come to a moment when you need to manage hundreds of tables with millions of records total - then you will see the difference.
So, final summation:
ORM is what you need, there are many solutions, I know of two really good: Doctrine 1 or 2 and Propel.
P.S.: ORM is an independant part of your system, so you don't really need to use a specific framework, Zend can be configured to work with Doctrine wonderfully :)
It used to be that within a CodeIgniter model you couldn't access another model.
$this->load->model('bar');
$this->bar->something();
Is this still valid, or have they changed it?
Those are some VERY long answers for a simple question.
Short answer: This is now fully supported. Cross-load away if you feel like it!
I strongly disagree with the notion that a "model" should only encapsulate a database table with simple CRUD operations. As noted in the Wikipedia article:
http://en.wikipedia.org/wiki/Model-view-controller#As_a_design_pattern
...that application layer is intended to do more than simply act as a single database table abstraction. Think about the meaning of the word "controller" -- it should act more as a director rather than being the entire application in and of itself. A "model" is a place for business logic. Most large scale applications in fact hold much of their business logic in the database itself (in the form of triggers, stored procedures, foreign keys, etc.).
I think the misunderstanding of what a "model" is is partly caused by the same (over-)hype of "MVC", without carrying with it much understanding of the concepts themselves. Kinda like how empty "AJAX" is, or even more easy, "Web 2.0". For better or worse, plenty of script kiddies have jumped on the MVC wagon, and since the simple howtos and example scenarios don't do much more than tell you to put your database code in the "model", the misuse of that layer as only a database abstraction has become commonplace. Now you read posts all over the Internet calling it "unpure", "dirty", "hackish" to put any business logic in a model. That's WRONG. Misinformed.
The easy example is to think about foreign keys: even if you only want your "model" to be a database model, if you want to be "pure", "correct" or what have you, you really should be enforcing referential integrity therein. Thanks to the lack of real foreign key support in MySQL for many years, web applications grew up without anyone worrying about referential integrity at all. Fits the script kiddie lifestyle I guess. Anyhow, for even this simplified view of a model to be able to maintain foreign key validity, a model then has to work with others (or, especially if a framework like CodeIgniter does not let you do so, you have to write queries to other tables, sometimes duplicating queries elsewhere - THAT is bad style).
Therefore, I believe this to be a shortcoming of CodeIgniter. I understand that it might not be an easy fix, but it's certainly a disappointing oversight.
So what I did was take the example code above and abstract it into a helper so that I now have a function that works almost identically to the normal $this->load->model() functionality. Here it is (put it into a helper that is auto-loaded and you can use it in any model):
/**
*
* Allow models to use other models
*
* This is a substitute for the inability to load models
* inside of other models in CodeIgniter. Call it like
* this:
*
* $salaries = model_load_model('salary');
* ...
* $salary = $salaries->get_salary($employee_id);
*
* #param string $model_name The name of the model that is to be loaded
*
* #return object The requested model object
*
*/
function model_load_model($model_name)
{
$CI =& get_instance();
$CI->load->model($model_name);
return $CI->$model_name;
}
It's possible, but not ideal and considered bad and more for "quick fix" than ideal or pure implementation.
class Location extends Model{
public function get($ID){
// Get main CI object handle and load model
$CI =& get_instance();
$CI->load->model('LocationType');
// Call new model functions using handle to main CI object
$CI->LocationType->setID($result->LocationTypeID);
$CI->LocationType->setTitle($result->TypeTitle);
$this->_locationType = $CI->LocationType;
//Other Stuff
}
}
Anytime you're using the main CI object like this is probably a bad idea. Try to re-think your layout and just pass data to/from your controller to the models.
http://codeigniter.com/forums/viewthread/69833/
You can load models from models as Phil Sturgeon says, but you have to be careful of dependencies if you load the models in the model constructor: if model A uses model B and model B uses model A, when you try to load one or the other, you'll go into an infinite loop.
In situations like this in Code Igniter I prefer one of two posiibilities:
1) Have model's attribute and setter like this:
class X extends Model {
var $Y_model;
public function setY($Y) {
$this->Y_model = $Y;
}
public function doItRightNow($a,$b) {
$list = $this->Y_model->getSomeList($a,$b);
// ...
}
// ...
}
And then use this setter before other methods to give an instance of other model so it can be used by methods.
$this->load->model('X');
$this->load->model('Y');
$this->X->setY($this->Y);
$this->X->doItRightNow($something,$somethingElse);
2) To have a parameter in method by which I will give an other model instance from controller.
class X extends Model {
public function doItRightNow($a,$b,$Y_model) {
$list = $Y_model->getSomeList($a,$b);
// ...
}
// ...
}
And use it like this:
$this->load->model('X');
$this->load->model('Y');
$this->X->doItRightNow($something,$somethingElse,$this->Y);
I think that these are more clean possibilities.
Which way to use depends on how many methods need to access other model. If there are one or two it might be better to give it as a method parameter. If more - I think it's better to have a class attribute and setter.
And in elegant way you can give one model or another depending on some condition - if they both partially implement same interface with the same kind of data returned (it's rarely useful, but it can be sometimes).
I think it's generally better the write libraries that access the models and then include the libraries in your model if need be.
For instance, if you needed to check if someone is authorized to go through with a certain CRUD action, you might want to include whatever authentication library you are using (its probably auto-included in most cases). You wouldn't necessarily want to access the model directly--it just seems dirty and improper.
I think the preferred way is to do what you need to do in your controller and pass the results from one model's method(s), if need be, to your other model's method(s).
Regardless, I can't see why it wouldn't be possible to include one model into another, per se. I don't think you can do it with the syntax you are showing, though. You would have to do it some other convoluted way. In any case, IMO, it's bad practice to include a model directly into another model.
In CI 2.0 you can just call one model directly from another.
Better to create a helper function instead of calling the function from another model so that it can be used in 2 models at a time and code can be reused.