I have two classes, with one storing multiple references to the other. After I create a new instance of the second class, I might have to change properties on it later and I would like it to update all references to that object. Is there a way to make the object instances always pass by reference so that when a property gets updated in the object it also updates all other references to it?
class Users {
private $members = array();
public function addMember($id) {
$member = new Member($id);
array_push($members, $member);
return $member;
}
}
class User {
public $id;
__construct($id) {
$this->id = $id;
}
public function addProperty($propName, $propValue) {
$this->{$propName} = $propValue;
}
}
So currently with this setup I can create a new member and add them to the members array:
$users = new Users();
$user = $users->addMember(1);
But if I try to add a new property to the member as so:
$member->addProperty("job_title", "developer");
The property doesn't appear in the members array because the member is not being passed by reference.
See this:
public function addMember($id) {
$member = new Member($id);
....
Here you create an object of class Member, but I think you want
$member = new User($id);
I am building a web application in PHP. Generally, objects will relate to a specific row in the database. I want to write some classes to allow manipulation of those rows. How's this as an example:
Edit: I think this sums up what I'm trying to do, just looking for a practical example in PHP http://martinfowler.com/eaaCatalog/dataMapper.html
// Create a new company record in the database
$data = array(
'name' => 'My Test Company';
);
try {
$companyId = Company_Common::create($data);
} catch (Exception $e) {
exit('Error: ' . $e->getMessage() . "\n");
}
// Load the record from the database
try {
$company = new Company($companyId);
} catch (Exception $e) {
exit('Error: ' . $e->getMessage() . "\n");
}
// Rename the company
$company->name = 'Company Name New';
$company->save();
class Company_Common {
function create($data) {
// add code here to check required fields, insert company record into database, then return row id
}
}
class Company {
public function __construct($id) {
// add code here to load database row from company table based on id
// throw exception if id not found in database table
if (mysql_num_rows($result) < 1) { // something like this depending on database
throw new Exception('Company Not Found');
}
// dynamically set properties from database row
$row = mysql_fetch_assoc($result);
foreach($row as $key => $value) {
$this->{$key} = $value;
$this->original->{$key} = $value;
}
}
function save() {
// load another copy from database
$original = new Company($this->id);
// add code here to compare $this with $original, build update query, and update any fields that have changed
}
function delete() {
// add code here to delete company record from database
}
}
Beyond this the above class will include functions like getContactIds, which would retrieve a bunch of row ids from the contacts table, based on the company id.
I'm really just after some feedback on what others think of this approach, and what should or could be done differently/more efficiently. Thanks.
I personally prefer to create an explicit function call to pull data from the database rather than putting it in the constructor. This allows me to create dummy instances of the class for testing and mocking purposes. A general class might look like this:
class Car {
// optionally prevent cars from being instantiated any other way
private function __construct() {}
// get car from db
public static function find($id) {
// your db stuff here
$data = $db->where('id', $id)->get();
return self::make($data);
}
// mock car - create instance and return with data
public static function make(array $data) {
$instance = new self();
return $instance->fill($data);
}
// set the provided data to the model
private function fill(array $data) {
$this->make = isset($data['make']) ? $data['make'] : false;
$this->model = isset($data['model']) ? $data['model'] : false;
return $this;
}
public function save() {
// save to db
}
}
and usage would be:
// car with data from db
$car = Car::find(1);
// car with data from input
$make = $_GET['make'];
$model = $_GET['model'];
$car = Car::make(array('make' => $make, 'model' => $model));
$car->save();
// car with no data
$car = Car::make(array());
The main benefit here is that you can create mock objects for unit tests and you can create a model with data from sources other than the db
Learn about PDO!
For this I love using PDO::FETCH_CLASS as it allows for cool functions. consider this class:
class myClass
{private $foo,$bar;}
let's say I have a table with field foo and bar, and I want to instantiate an object of this class with those field. I'll do like this:
$stmt = $db->prepare( "SELECT foo,bar,'magic' as custom FROM table WHERE id=28");
$stmt->setFecthMode(PDO::FETCH_CLASS,'myClass');
$object = $stmt->fetch();
var_dump($object);
$object is now an instantiated object of myClass, with three properties, two from the database and one i've manually entered in the sql
Here is the way I save those objects back in the database using oop destructor
My question is referenced to Doctrine many-to-many relations and onFlush event
Author entitiy: http://pastebin.com/GBCaSA4Z
Book entity: http://pastebin.com/Xb2SEiaQ
I run this code:
$book = new \App\CoreBundle\Entity\Books();
$book->setTtile('book title: '.uniqid());
$book->setIsbn('book isbn: '.uniqid());
$author = new \App\CoreBundle\Entity\Authors();
$author->setName('author title: '.uniqid());
$author->addBook($book);
$entityManager->persist($author);
$entityManager->flush();
In this way all is OK.
Doctrine generate three queries: create author, create book and create links at author_books.
But I need to detach and serialize all entities at doctrine onFlush event and save them at NoSQL database. And then other script will unserialize all these entities and save them to database. And in this way Doctrine generate only two SQL queries: create book and create author. What should I do to doctrine also generate SQL query for linking table?
Here part of the script that unserialize entities:
foreach ($serializedEntities as $serializedEntity)
{
$detachedEtity = unserialize($serializedEntity);
$entity = $entityManager->merge($detachedEtity);
//$entityManager->persist($entity)
}
$entityManager->flush();
UPDATE: and I always get error like this:
Notice: Undefined index: 000000002289c17b000000003937ebb0 in /app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 2776
You can define a behavior of each entity with inherited class like :
class FieldRepository extends EntityRepository{
define all specific methods
}
so each entity will be saved as defined on class
You can use also the helpers concepts, this will solve your problem faster and easy
OnFlush ---> dispache event 'onFlush' --> define behavior on the helper class
Just a helper class exemple
// Helper book class
class Booking{
public function __construct($container)
{
$this->container = $container;
$this->club_interval = $container->get('X.interval');
$this->security_context = $container->get('security.context');
// your entity manager
$this->em = $container->get('doctrine.orm.entity_manager');
$this->session = $container->get('session');
$this->translator = $container->get('translator');
$this->event_dispatcher = $container->get('event_dispatcher');
$this->price = 0;
}
public function serialize()
{
$this->session->set('booking', serialize($this->booking));
}
public function unserialize()
{
$b = unserialize($this->session->get('booking'));
$partner = null;
foreach ($b->getUsers() as $u) {
$partner = $this->em->getReference('X:User', $u->getId());;
}
$field = $this->em->getReference('X:Field', $b->getField()->getId());
$user = $this->em->getReference('X:User', $b->getUser()->getId());
$this->buildBooking($field, $b->getFirstDate(), $b->getEndDate(), $user, $partner, $b->getGuest());
}
///////////////////////////////////////////////
// here you can choose your database manager check __constructor
////////////////////////////////////////////
public function save(){
$this->em->persist($this->booking);
$this->em->flush();
if ($this->booking->getStatus() >= \X\BookingBundle\Entity\Booking::CONFIRMED) {
$event = new \X\BookingBundle\Event\FilterBookingEvent($this->booking);
$this->event_dispatcher->dispatch(\X\BookingBundle\Event\Events::onBookingConfirm, $event);
}
$this->em->flush();
return $this->booking;
}
so you can trigger this class method directly form you Symfony2 controller, use save() instead of persist()
or using event dispatcher but more tricky
if (false === $this->get('security.context')->isGranted('ROLE_USER')) {
throw new AccessDeniedException();
}
$b = $this->get('X_helper.booking');
$b->unserialize();
$b->setStatus(\X\BookingBundle\Entity\Booking::PENDING);
/////////////////////////////////////
// here use helper's method save instead of flush
$b->save();
return $this->redirect($this->generateUrl(' route '));
I'm trying to decide whether to create many classes for each content type I have in my application/database or just stick with procedural code.
Version 1:
make a class for each object collection:
class App{
protected $user_collection;
function getUserCollection(){
if(!isset($this->user_collection)
$this->user_collection = new UserCollection($this);
return $this->user_collection;
}
// ...
}
class UserCollection{
function __construct(App $app){
$this->app = $app;
}
function getUser($user){
return new User($this->app, $user);
}
function getUsers($options){
$users = $this->app->getDatabase()->query($options);
foreach($users as &$user)
$user = new User($this, $user);
return $users;
}
// ...
}
which I'm using like:
$app = new App();
echo $app->getUserCollection()->getUser('admin')->email_address;
version 2:
keep all methods in a single class
class App{
function getUsers($options){
$users = $this->getDatabase()->query($options);
foreach($users as &$user)
$user = new User($this, $user);
return $users;
}
function getUser($user){
return new User($this, $user);
}
// ...
}
used like:
$app = new App();
echo $app->getUser('admin')->email_address;
version 3:
make getUsers() a a static method in the "User" class (the method instantiates a new User object):
$app = new App();
echo User::getUser($app, 'admin')->email_address;
Which way should I go? The "user" object is just an example, App has other objects too, like "database", "pages" etc.
I would use your version 1, but I would make getUser() and getUsers() methods of App.
This gets rid of the awkward getUserCollection() call, because instead inside the getUser() and what not you just call $this->user_collection.
Personnaly, I often used the second one with method like this:
class user {
/**
* Load object from ...
*/
public function load($userId) {}
/**
* Insert or Update the current object
*/
public function save() {}
/**
* Delete the current object
*/
public function delete() {
// delete object
// Reset ID for a future save
$this->UserID = null;
}
/**
* Get a list of object
*/
public static function getList() {
// Make your search here (from DB)
// Put rows into new "SELF" object
$list = array();
foreach($rows as $row) {
$obj = new self();
$obj->populate($row);
$list[$obj->UserID] = $obj; // Associative array or not...
}
}
}
Like you can see, I set my "getList" function static to simply access like this:
$listUsers = user::getList();
OK, it's very simple but work in most case of simple app.
I have been working over an year with Magento and have learned it good enough. Now I want to learn Zend, and I'm stuck with models.
I'm used to have entities and collection of entities in Magento, and it's likely that I'll want to use Zend_Db_Table, Zend_Db_Table_Row and/or Zend_Db_Table_Rowset. What I am confused of is the role each class.
I know that I can extend each class, and I understand that in my Product_Table class (that extends Zend_Db_Table_Abstract) it's possible to have private methods that will tell Zend what classes to use for rows and rowsets, however I'm not feeling comfortable with it.
Having this code in Magento:
Example 1
// I understand that maybe I'll use the `new` keyword instead
// Mage::getModel() is only for exemplification
$product = Mage::getModel('catalog/product');
$product->setName('product name');
$product->setPrice(20);
$product->save();
if($id = $product->getId()){
echo 'Product saved with id' . $id;
}
else{
echo 'Error saving product';
}
Example 2
$collection = Mage::getModel('catalog/product')->getCollection();
// this is the limit, I'm ok with other method's name
$collection->setPageSize(10);
$collection->load()
foreach($collection as $product){
echo $product->getName() . ' costs ' . $product->getPrice() . PHP_EOL;
}
How I can implement something similar in Zend Framework? Alternatively if this is a really a bad idea, what are the best practices to implement models in Zend Framework?
Thanks
The Zend team, as mentioned elsewhere, thinks differently about the Model layer than most other PHP Framework creators. Their current thoughts on "the best" way to use their raw tools to provide a Database backed Entity Model can be found in the quick start guide.
That said, most people's solution to Models in Zend Framework is bootstrapping Doctrine.
Here is how I, personally, implement models. I'll use a real life example: my User model.
Whenever I create a model, I use two files and two classes: the model itself (e.g. Application_Model_User) and a mapper object (e.g. Application_Model_UserMapper). The model itself obviously contains the data, methods for saving, deleting, modifying, etc. The mapper object contains methods for fetching model objects, finding objects, etc.
Here are the first few lines of the User model:
class Application_Model_User {
protected $_id;
protected $_name;
protected $_passHash;
protected $_role;
protected $_fullName;
protected $_email;
protected $_created;
protected $_salt;
// End protected properties
For each property, I have a getter and setter method. Example for id:
/* id */
public function getId() {
return $this->_id;
}
public function setId($value) {
$this->_id = (int) $value;
return $this;
}
I also use some standard "magic methods" for exposing public getters and setters (at the bottom of each model):
public function __set($name, $value) {
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid user property');
}
$this->$method($value);
}
public function __get($name) {
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid user property');
}
return $this->$method();
}
public function setOptions(array $options) {
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
Example save method:
I validate inside the save() method, using exceptions when the information fails to validate.
public function save() {
// Validate username
if (preg_match("/^[a-zA-Z](\w{6,15})$/", $this->_name) === 0) {
throw new Application_Exception_UserInfoInvalid();
}
// etc.
$db = Zend_Registry::get("db");
// Below, I would check if $this->_id is null. If it is, then we need to "insert" the data into the database. If it isn't, we need to "update" the data. Use $db->insert() or $db->update(). If $this->_id is null, I might also initialize some fields like 'created' or 'salt'.
}
For the mapper object, I have at least two methods: a method that returns a query object for selecting objects, and one that executes the query, initializes and returns objects. I use this so I can manipulate the query in my controller for sorting and filtering.
EDIT
Like I said in my comments, this post: http://weierophinney.net/matthew/archives/202-Model-Infrastructure.html was the inspiration for my current Model implementation.
More options
You can also use Zend_Form to do validation, instead of rolling your own: http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html. I personally don't like this option since I think that Zend_Form is awkward to use and hard to precisely control.
When most people first learn Zend Framework, they learn to subclass Zend_Db related classes. Here is an article that demonstrates this: http://akrabat.com/zend-framework/on-models-in-a-zend-framework-application/
I mentioned that I don't like doing this. Here are a few reasons why:
It's difficult to create models that involve derived/calculated fields (i.e. data populated from other tables)
I found it impossible to incorporate access control (populated from my database)
I like having full control over my models
EDIT 2
For your second example: You can use Zend_Paginator for this. I mentioned that, in your wrapper, you create a method that returns a database query object for selecting objects. Here's my simplified but working user mapper:
class Application_Model_UserMapper {
public function generateSelect() {
$db = Zend_Registry::get("db");
$selectWhat = array(
"users_id",
"name",
"role",
"full_name",
"email",
"DATE_FORMAT(created, '%M %e, %Y at %l:%i:%s %p') as created",
"salt",
"passhash"
);
return $db->select()->from(array("u" => "users"), $selectWhat);
}
public function fetchFromSelect($select) {
$rows = $select->query()->fetchAll();
$results = array();
foreach ($rows as $row) {
$user = new Application_Model_User();
$user->setOptions(array(
"id" => $row["users_id"],
"name" => $row["name"],
"role" => $row["role"],
"fullName" => $row["full_name"],
"email" => $row["email"],
"created" => $row["created"],
"salt" => $row["salt"],
"passHash" => $row["passhash"]
));
$results[] = $user;
}
return $results;
}
}
To handle the paginator, I write a custom Paginator plugin and save it to library/Application/Paginator/Adapter/Users.php. Be sure you have your appnamespace and autoloaderNamespaces[] setup correctly in application.ini. Here is the plugin:
class Application_Paginator_Adapter_Users extends Zend_Paginator_Adapter_DbSelect {
public function getItems($offset, $itemCountPerPage) {
// Simply inject the limit clause and return the result set
$this->_select->limit($itemCountPerPage, $offset);
$userMapper = new Application_Model_UserMapper();
return $userMapper->fetchFromSelect($this->_select);
}
}
In my controller:
// Get the base select statement
$userMapper = new Application_Model_UserMapper();
$select = $userMapper->generateSelect();
// Create our custom paginator instance
$paginator = new Zend_Paginator(new Application_Paginator_Adapter_Users($select));
// Set the current page of results and per page count
$paginator->setCurrentPageNumber($this->_request->getParam("page"));
$paginator->setItemCountPerPage(25);
$this->view->usersPaginator = $paginator;
Then render the paginator in your view script.
I do something similar to SimpleCode's way. My style derives from Pádraic Brady. He has multiple blog posts but the best and quickest resource of his is a online book he wrote: Survive the Deep End!. This link should take you straight to his chapter on Models, Data Mappers, and other cool goodies such as Lazy Loading. The idea is the following:
You have entities such as a User with The properties are defined in an array. All your entities extend an abstract class with magic getter/setters that get from or update this array.
class User extends Entity
{
protected $_data = array(
'user_id' => 0,
'first_name' => null,
'last_name' => null
);
}
class Car extends Entity
{
protected $_data = array(
'car_id' => 0,
'make' => null,
'model' => null
);
}
class Entity
{
public function __construct($data)
{
if(is_array($data))
{
$this->setOptions($data);
}
}
public function __get($key)
{
if(array_key_exists($key, $this->_data)
{
return $this->_data[$key];
}
throw new Exception("Key {$key} not found.");
}
public function __set($key, $value)
{
if(array_key_exists($key, $this->_data))
{
$this->_data[$key] = $value;
}
throw new Exception("Key {$key} not found.");
}
public function setOptions($data)
{
if(is_array($data))
{
foreach($data as $key => $value)
{
$this->__set($key, $value);
}
}
}
public function toArray()
{
return $this->_data;
}
}
$user = new User();
$user->first_name = 'Joey';
$user->last_name = 'Rivera';
echo $user->first_name; // Joey
$car = new Car(array('make' => 'chevy', 'model' => 'corvette'));
echo $car->model; // corvette
Data Mappers to me are separate from the Entities, their job is to do the CRUD (create, read, update, and delete) to the db. So, if we need to load an entity from the db, I call a mapper specific to that entity to load it. For example:
<?php
class UserMapper
{
$_db_table_name = 'UserTable';
$_model_name = 'User';
public function find($id)
{
// validate id first
$table = new $this->_db_table_name();
$rows = $table->find($id);
// make sure you get data
$row = $rows[0]; // pretty sure it returns a collection even if you search for one id
$user = new $this->_model_name($row); // this works if the naming convention matches the user and db table
//else
$user = new $this->_model_name();
foreach($row as $key => $value)
{
$user->$key = $value;
}
return $user;
}
}
$mapper = new UserMapper();
$user = $mapper->find(1); // assuming the user in the previous example was id 1
echo $user->first_name; // Joey
This code is to give an idea of how to architect the code in this way. I didn't test this so I may have created some typos/syntax errors as I wrote it. Like others have mentioned, Zend lets you do what you want with Models, there is no right and wrong it's really up to you. I usually create a table class for every table in the db that I want to work with. So if I have a user table, I usually have a User entity, User Mapper, and a User Table class. The UserTable would extend Zend_Db_Table_Abstract and depending on what I'm doing won't have any methods inside or sometimes I'll overwrite methods like insert or delete depending on my needs. I end up with lots of files but I believe the separation of code makes it much easier to quickly get to where I need to be to add more functionality or fix bug since I know where all the parts of the code would be.
Hope this helps.
Folder Structure
application
--models
----DbTable
------User.php
--controllers
----IndexController.php
--forms
----User.php
--views
----scripts
------index
--------index.phtml
application/models/DbTable/User.php
class Application_Model_DbTable_User extends Zend_Db_Table_Abstract
{
protected $_name = 'users';
protected $_primary = 'user_id';
}
application/forms/User.php
class Form_User extends Zend_Form
{
public function init()
{
$this->setAction('')
->setMethod('post');
$user_name = new Zend_Form_Element_Text('user_name');
$user_name->setLabel("Name")->setRequired(true);
$user_password = new Zend_Form_Element_Text('user_password');
$user_password->setLabel("Password")->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Save');
$this->addElements(array(
$user_name,
$user_password,
$submit
));
}
}
application/controllers/IndexController.php
class IndexController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
$form = new Form_User();
if($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost()))
{
$post = $this->getRequest()->getPost();
unlink($post['submit']);
$ut = new Application_Model_DbTable_User();
if($id = $ut->insert($post))
{
$this->view->message = "User added with id {$id}";
} else {
$this->view->message = "Sorry! Failed to add user";
}
}
$this->view->form = $form;
}
}
application/views/scripts/index/index.phtml
echo $this->message;
echo $this->form;