CakePHP Model instances confusion - php

After debugging quite a bit, I noticed a really strange behaviour inside of CakePHP's (2.x) Model usage:
When I changed the Model ID and used read(), on a completly different object instance with a relation to the same Model, it overwrites the old Model data.
// set the user, by using the 'User' model
$this->User->id = 1;
$this->User->read();
print_r($this->User->data); // works correctly
$instance = new Notification(); // this has a relation to the 'User' model
print_r($instance->User->data); // == $this->User->data! why?!
$instance->User->id = 2;
$instance->User->read();
print_r($this->User->data); // == $instance->User->data!
Why are those Models connected with each other? Shouldnt they be completly separated, since it's a new instance? I mean, I'm setting the 'User' model for the Notification, not for $this
And if that's default behaviour - how can I read() data into different instances, whitout changing other models? Do I really need to manually create a new 'User' instance and store it somewhere in $instance to avoid this behaviour? That sounds rather ugly to me.

Model instances are singletons
The following two objects in the question are identical:
$this->User
$instance->User
Because they are literally the same object, the path used to access an object doesn't modify the behavior of the (User) object itself.
That's simply how ClassRegistry::init works - it stores a reference to model instances - and will return the same object when queried for the same alias (className) again.
Don't create models using new
Doing that is not normal - and will likely cause problems or at least confusion in the future. To get a reference to the Notification model, use $uses, loadModel or ClassRegistry::init as appropriate.
Don't use Model::read
Do I really need to manually create a new 'User' instance
Absolutely not, that's not how models are intended to work with CakePHP. A model class is effectively the interface to the database, it's not a representation of a single row (except when calling save).
The simplest way to avoid a significant number of problems is to not use Model::read at all, and instead use any appropriate find call; A more complete code example would permit a more specific answer.

Related

PHP MVC - Model needs to access data from another model

I have built a small PHP MVC framework and just want to clarify the best way to get data from one model into another. For example:
I have a Users_model that contains a method called get_users().
I also have Communications_model that needs to get specific or all user data and as such needs to access the get_users() method from the Users_model.
Is it best practice to:
a) Instantiate the Users_model in a controller and pass the data from the get_users() method into the Communications_model?
b) Instantiate the Users_model inside the Communications_model and run get_users() from there, so it can be accessed directly?
c) Another way?
Many thanks for any help.
It depends of your motive behind this.
If you want effect on result, then using well know library, like Doctrine etc. should be your choice.
If you want to learn design patterns, then you should get read about ActiveRecord or DataMapper + Repository patterns. Then implements both and check out.
If you want your code, this way - ORM should represent relations of data, then you should ask what it more important? If you menage communication (bus, train), then user can be there assigned and getting users from communication is OK. If user have communication (like car), then relation is reversed.
All depends, what is you motive behind this. Using library, like Doctrine, could you help you running you application. If you want learn design patterns, then check out both options to get some experience.
What you call "users model" is a repository. And what you call "communication model" looks like a service.
Your communication service should have the user repository passed in constructor as a dependency.
I honestly think, that a huge part of your confusion is that you try to call all of those things "models". Those classes are not part of the same layer. You migth find this answer to be useful.
All are possible ways but what I usually do is, whenever there is any function that I think would be reused a number of times by a number of objects, I declare it as static.
It would save the effort of playing with object declaration and would be easily accessible by ClassName::function();
Again, it's a design choice, usually objects are declared right there in the controller and used as per the need but just to save declaration of objects again and again I follow the approach of declaring function static.
The simple principle here is using the __construct() (constructor) to build the object with the relevant properties from the Database. The User Model will have a static function (therefore accessible through any scope) to create an array of instanced objects by simply passing the model data through a new self() which returns the instance.
The concept is you end up with an array of User_Model instances each being a build of the Database columns to properties. All that's left is to create the Database Model and the functions to retrieve the columns and data.
class Communications_Model {
private $_all_users;
public function getUsers() {
$this->_all_users = Users_Model::loadAllUsers();
}
}
class Users_Model {
private $_example_property;
public function __construct($user_id) {
$data = SomeDatabaseModel::getConnection()->loadUserFromDatabase((int)$user_id);
$this->_example_property = $data['example_column'];
}
public static function loadAllUsers() {
$users = array();
foreach(SomeDataModel::getConnection()->loadAllUsers() as $data) {
$users[] = new self($data['user_id']);
}
return $users;
}
}
Of course, now, you have a $_all_users; property that has an array of instanced User Models containing the data.

MVC: Having static method in models

Concise: How I can avoid using static methods in a model?
Loquacious: Suppose I have a user class. Having userID I can get user name by (new user($userID))->getUserName(). Fine, what if I want to lookup a user? (new user())->lookup($uname, $pass). Still fine, but the latter case could be done via a simple static method user::lookup($uname, $pass)!
Some thoughts:
It's OK! Use (new object())->method() whenever you want. So should I create a hollow object to call a function?
Move this function out of your model. If it needs a DB lookup, where is better than Model context?
Define it as a static method. So isn't it lame to have a mixture of public and static methods in a class?
Side note: I've searched this question, no avail!
Move this function out of your model. If it needs a DB lookup, where is better than Model context?
Yes, indeed, this is the best way to solve the problem.
Currently your User class violates single responsibility principle which basically, says "one task - one class".
Right now your User describes user entity/state and handles persistence (in your case - retrieval from database). See, two things.
I suggest you create another class that is going to handle persistence tasks, like add/update/delete user. The simplest solution is to create a primitive repostitory, like this:
<?php
class UserRepository
{
public function addUser(User $user);
public function updateUser(User $user);
public function deleteUser(User $user);
public function getUserById($id);
}
Then retrieval of user can be done in the following manner:
// get an instance of this repository class
$userRepository = new UserRepository;
// ask it to find and return user from the database by ID
$user = $userRepository->getUserById($_GET['id']);
Easy to read, easy to handle, right?
This UserRepository class is actually a primitive implementation of Repository Pattern. UserRepository emulates an in-memory collection of all of your users, hiding implementation inside. It hides actual persistence mechanism from you as user: imagine, your coleague would write this class and you're just using its methods, like UserRepository::getById(1) - you don't even know/care if it grabs data from files/db/API. That's neat. )
This particular implementation is described very clearly in Kristopher Wilson's book "The Clean Architecture in PHP", which I highly recommed for you to read: it will take you two-three evenings, and push you to the next level.
You can extend the list of methods, of course, add lookups, etc.
class UserRepository
{
public function getByCompany(Company $company);
public function getByEmail($email);
public function countTotal();
}
In fact, every time you need to grab/add/update user in the database, you should do it via this repository.
I would like to emphasize that this is a simple implementation of the pattern, particularly, if you compare it to what Martin Fowler describes as Repository. However, in most cases it's totally fine.
It's OK! Use (new object())->method() whenever you want. So should I create a hollow object to call a function?
depends on how much creating an instance will cost
Move this function out of your model
Factory Pattern comes in mind here.
notes here:
What happens when $userID in the first call do not exists?
Isnt your lookup() method not creating 2 instances at one call, first for lookup, second the found one that is returned?
A FactoryPattern for example can have findByID() or findByName() and return an UserObject. And all that should not depend on this syntax at all: (new object())->method(), that is nice, but not always best practise.

PHP OOP: Avoid Singleton/Static Methods in Domain Model Pattern

I understand the importance of Dependency Injection and its role in Unit testing, which is why the following issue is giving me pause:
One area where I struggle not to use the Singleton is the Identity Map/Unit of Work pattern (Which keeps tabs on Domain Object state).
//Not actual code, but it should demonstrate the point
class Monitor{//singleton construction omitted for brevity
static $members = array();//keeps record of all objects
static $dirty = array();//keeps record of all modified objects
static $clean = array();//keeps record of all clean objects
}
class Mapper{//queries database, maps values to object fields
public function find($id){
if(isset(Monitor::members[$id]){
return Monitor::members[$id];
}
$values = $this->selectStmt($id);
//field mapping process omitted for brevity
$Object = new Object($values);
Monitor::new[$id]=$Object
return $Object;
}
$User = $UserMapper->find(1);//domain object is registered in Id Map
$User->changePropertyX();//object is marked "dirty" in UoW
// at this point, I can save by passing the Domain Object back to the Mapper
$UserMapper->save($User);//object is marked clean in UoW
//but a nicer API would be something like this
$User->save();
//but if I want to do this - it has to make a call to the mapper/db somehow
$User->getBlogPosts();
//or else have to generate specific collection/object graphing methods in the mapper
$UserPosts = $UserMapper->getBlogPosts();
$User->setPosts($UserPosts);
Any advice on how you might handle this situation?
I would be loathe to pass/generate instances of the mapper/database access into the Domain Object itself to satisfy DI - At the same time, avoiding that results in lots of calls within the Domain Object to external static methods.
Although I guess if I want "save" to be part of its behaviour then a facility to do so is required in its construction. Perhaps it's a problem with responsibility, the Domain Object shouldn't be burdened with saving. It's just quite a neat feature from the Active Record pattern - it would be nice to implement it in some way.
What I do, albeit maybe not the best course of action, is to have a clear naming convention for my classes, FI: user_User is the domain object and user_mapper_User is it's mapper.
In my parent domainObject class I code the logic to find it's mapper.
Then you have a couple options to delegate to it, an obvious one would be to use the __call() method in domainObject.

How to create a method requiring unrelated data from multiple tables in CakePHP

I have to make a view that shows unrelated data from multiple tables. I am new to cakePHP (and PHP in general) and as far as my understanding goes, each model is a depiction of just one table. I know that we can define associations with other tables, but in my case I need to give access to data that is no way related to the model who's view will be opened.
Example:
Say there is a blogging platform and we are currently viewing a post. (Model - Post, function - Read). Now I want a list of (Say) subscribers of our newsletter. This data is not related to the model and hence, I don't think the data will be accessible to the controller. Please tell me how to get this data in view directly.
In your controller, when defining the class, add a class attribute $uses to tell your controller which models to load.
class SomeController extends AppController {
public $name = 'Some';
public $uses = array( 'Model1', 'Model2' );}
And then, in your method, you just call that model:
$result = $this->Model1->find('all');
$result2= $this->Model2->find('all');
There are a few ways to do this. Here is gwoo's synopsis of how they differ and when to use which:
App::import() only includes the file.
So you would new to create a new
instance every time. This is not
recommended
ClassRegistry::init() loads the file,
adds the instance to the a object map
and returns the instance. This is an
easy and convenient way to access
models.
Controller::loadModel(); Uses
ClassRegistry::init() adds the model
to a property of the controller and
also allows persistModel to be
enabled.
While you "can" do any of these
things, you should ask yourself why
you are creating dependencies on
models that are not natural to the
controller. If you "have" to do use
any of these, then I would do so in
reverse order of the way i described
them. IE, Controller::loadModel ()
then CR::init() and actually I never
use App::import() for models. Hope
this helps.
See this page for the full discussion: http://groups.google.com/group/cake-php/browse_thread/thread/137c57b4eb010317
In addition, some other answers have suggested including the unrelated model in the $uses array, but I would avoid this method as it is really intended to tell the model which database table to use and implies that its members are central to the purpose of the model, which is not the case in the situation you describe.
If you're going to use it in a couple of functions in the same model, you should specify it inside the Controller via:
$uses = array('Post', 'Suscriber');
Now, if it is a elemnt of the layout, you should set it on an element.
In the view:
$this->renderElement('suscribers-list');
Now you must create a suscribers-list.ctp file in views/elements. From there import the model:
App::Import('Model', 'Suscriber');
$this->Suscriber = new Suscriber();
$suscribers = $this->Suscriber->find('all');
pr($suscribers);
It's not pretty, but it's what works for me. I don't know if there's another way.
You can do this with less code, and slightly simpler. You can make use of the AppController::loadModel('ModelName'). (reference) This method takes care of initilization step that metrobalderas suggested so then if becomes
$this->loadModel('Subscriber');
$subscribers = $this->Subscriber->find('all', ... );
There was a function that was deprecated in 1.2 that was just loadModel('ModelName'); but not the AppController method. Also it should be noted that you should not directly load models in an element, as that is not really in the spirit of MVC. Keep that in the model. Using this method rather the var $uses = array('ModelName'); does reduce the overhead of the unrelated models in methods that don't need it, as well as reduces some of the complications that can occur when using that approach.

Should I be extending this class? (PHP)

I'm creating an ORM in PHP, and I've got a class 'ORM' which basically creates an object corresponding to a database table (I'm aiming for similar to/same functionality as an ActiveRecord pattern.) ORM itself extends 'Database', which sets up the database connection.
So, I can call: $c = new Customer();
$c->name = 'John Smith';
$c->save();
The ORM class provides this functionality (sets up the class properties, provides save(), find(), findAll() etc. methods), and Customer extends ORM. However, in the future I may be wanting to add extra public methods to Customer (or any other model I create), so should this be extending ORM or not?
I know I haven't provided much information here, but hopefully this is understandable on a vague explanation, as opposed to posting up 300+ lines of code.
I agree with the other answers here - put the additional methods into a descendant class. I'd also add an asterisk to that though: each time you extend the class with extra methods, think about what you are trying to achieve with the extension, and think about whether or not it can be generalised and worked back into the parent class. For example:
// Customer.class.php
function getByName($name) {
// SELECT * FROM `customer` WHERE `name` = $name
}
// ** this could instead be written as: **
// ORM.class.php
function getByField($field, $value) {
// SELECT * FROM `$this->table` WHERE `$field` = $value
}
You're certainly thinking correctly to put your business logic in a new class outside your 'ORM'. For me, instead simply extending the ORM-class, I'd rather encapsulate it with a new, value object class to provide an additional degree of freedom from your database design to free you up to think of the class as a pure business object.
Nope. You should use composition instead of inheritance. See the following example:
class Customer {
public $name;
public function save() {
$orm = new ORM('customers', 'id'); // table name and primary key
$orm->name = $this->name;
$orm->save();
}
}
And ORM class should not extend Database. Composition again is best suited in this use case.
Yes, place your business logic in a descendant class. This is a very common pattern seen in most Data Access Layers generation frameworks.
You should absolutely extend the ORM class. Different things should be objects of different classes. Customers are very different from Products, and to support both in a single ORM class would be unneeded bloat and completely defeat the purpose of OOP.
Another nice thing to do is to add hooks for before save, after save, etc. These give you more flexibility as your ORM extending classes become more diverse.
Given my limited knowledge of PHP I'm not sure if this is related, but if you're trying to create many business objects this might be an incredibly time consuming process. Perhaps you should consider frameworks such as CakePHP and others like it. This is nice if you're still in the process of creating your business logic.
You're definitely thinking along the right lines with inheritance here.
If you're building an ORM just for the sake of building one (or because you don't like the way others handle things) than go for it, otherwise you might look at a prebuilt ORM that can generate most of your code straight from your database schema. It'll save you boatloads of time. CoughPHP is currently my favorite.
I have solved it like this in my Pork.dbObject. Make sure to check it out and snag some of the braincrunching I already did :P
class Poll extends dbObject // dbObject is my ORM. Poll can extend it so it gets all properties.
{
function __construct($ID=false)
{
$this->__setupDatabase('polls', // db table
array('ID_Poll' => 'ID', // db field => object property
'strPollQuestion' => 'strpollquestion',
'datPublished' => 'datpublished',
'datCloseDate' => 'datclosedate',
'enmClosed' => 'enmclosed',
'enmGoedgekeurd' => 'enmgoedgekeurd'),
'ID_Poll', // primary db key
$ID); // primary key value
$this->addRelation('Pollitem'); //Connect PollItem to Poll 1;1
$this->addRelation('Pollvote', 'PollUser'); // connect pollVote via PollUser (many:many)
}
function Display()
{
// do your displayĆ­ng for poll here:
$pollItems = $this->Find("PollItem"); // find all poll items
$alreadyvoted = $this->Find("PollVote", array("IP"=>$_SERVER['REMOTE_ADDR'])); // find all votes for current ip
}
Note that this way, any database or ORM functionality is abstracted away from the Poll object. It doesn't need to know. Just the setupdatabase to hook up the fields / mappings. and the addRelation to hook up the relations to other dbObjects.
Also, even the dbObject class doesn't know much about SQL. Select / join queries are built by a special QueryBuilder object.

Categories