So, lets say I have a record:
$record = new Record();
and lets say I assign some data to that record:
$record->setName("SomeBobJoePerson");
How do I get that into the database. Do I.....
A) Have the module do it.
class Record{
public function __construct(DatabaseConnection $database)
{
$this->database = $database;
}
public function setName($name)
{
$this->database->query("query stuff here");
$this->name = $name;
}
}
B) Run through the modules at the end of the script
class Record{
private $changed = false;
public function __construct(array $data=array())
{
$this->data = $data;
}
public function setName($name)
{
$this->data['name'] = $name;
$this->changed = true;
}
public function isChanged()
{
return $this->changed;
}
public function toArray()
{
return $this->array;
}
}
class Updater
{
public function update(array $records)
{
foreach($records as $record)
{
if($record->isChanged())
{
$this->updateRecord($record->toArray());
}
}
}
public function updateRecord(){ // updates stuff
}
}
A question you could ask yourslef is whether you want to reinvent the wheel or not. ORM layers like Propel or Doctrine already implement object to (R)DBMS mapping, so you might look at their implementation details.
Propel will use your second approach, they even keep flags on a field level to create just one update statement (which will keep database interaction at a minimum). You'll learn a lot if you study their source (or better yet, stop wasting your time and use their implementation - you won't regret it :p).
It depends on how you plan to implement... Doing all the writes at a single point (at the end of a request) is nice because it allows you to optimize your operations by consolidating queries where possible. But to do that you have to create something similar to a UnitOfWork to keep track of whats a delete/update/insert which can open a whole other can of worms.
On the other hand if you do it directly when you call the persistence method on the entity then you dont have to worry about that quite as much.
Both approaches though mean you have to have some way to make sure you always have the current data in your object but the work required to implementation that varies in complexity with he approach you choose.
Example A updates the database whenever setName is called. This function looks like a simple write accessor but it performs expensive actions when called (connecting to the database, executing a query, etc). These unintended site-effects make Example B far more appealing.
As a further example: Later on you might need a Validator class that examines a Record and ensures that the Record is in a valid state. But in order to examine the Record you must define it first by setting a name - so the Record will be persisted before you can validate it's state. Defining object state is not the same as persisting object state.
A data model approach might work better instead of a record-based approach. For instance:
class Model {
protected $_props= array();
public $changed= false;
static public $models= array();
function __set($name, $value) {
$this->changed= true;
$this->_props[$name]= $value;
}
function __construct() {
Model::$models[]= $this;
}
public function save() {
// Execute database query for saving the current Model
}
static public function update() {
foreach (Model::$models as $model) {
if ($model->changed) {
$model->save();
}
}
}
}
A model-based solution really shines when it comes to creating different Model types. For instance:
class Person extends Model {
public function save() {
// Execute person-specific write operations
}
}
class Doctor extends Person {
public function save() {
// Execute all Person write operations
parent::save();
// Save the extra bits that belong to a doctor
}
}
$person1= new Person();
$person->firstname= 'Jon';
$person->lastname= 'Skeet';
$doctor1= new Doctor();
$doctor1->firstname= 'House';
$doctor1->lastname= 'MD';
// Save all modified models
Model::update();
Though I rarely find use for these kind of mass update mechanisms. Write conditions are usually more specific.
Related
I am learning OO SOLID principles and design patterns and I want to do some practice on it. So I get the one problem from my ongoing project and try to design it. Please check whether it is implemented correctly or it's over-engineered or I implement it poorly. Your response is most important.
Problem
I have to manage sms and email campaigns in one system. I meant to say storing it in database and retrieving it etc.
So I think there will be something specific to the Campaign like created date status etc. Thus I have made the class named campaignmodel which is responsible for some common functions related to campaign
Class CampaignModel
{
public function save($data)
{
// add campaign specific data
// save the campaign.
}
public function get()
{
// select the row from database and return it.
}
}
then I make smscampaign and email campaign
class SMSCampaignModel extends CampaignModel
{
public function save($data)
{
// add sms specific data and
parent::save($data);
}
public function gets()
{
//fire the query to get the sms campaigns and returns it.
}
}
class EmailCampaignModel extends CampaignModel
{
public function save($data)
{
// add email specific data
parent::save($data);
}
public function gets()
{
//fire the query to get the email campaigns and returns it.
}
}
Now every campaign will have recipients and we have to store each recipient's status like he opens mail or mail/sms sent or failed etc. I think we will send the campaigns with many emails or numbers so I decided to create different database table for storing such details such as sms_campaign_log, email_campaign_log etc. I have created the interface for it
interface CampaignLogs
{
function insert_log();
function get_details();
}
class SmsCampaignLogs implements CampaignLogs
{
public function insert_log($data)
{
// get the number and status save it into the sms logs table.
}
public function get_details($campagin_id)
{
// get the logs from campagin table and return it.
}
}
class EmailCampaignLogs implements CampaignLogs
{
public function insert_log($data)
{
// get the number and status save it into the email logs table.
}
public function get_details($campagin_id)
{
// get the logs from campagin table and return it.
}
}
and lastly I think now I should use strategy pattern to implement it(I don't know whether it is correct or not).
class Campaign
{
private $log;
private $campaign_type;
public function __construct($campaign, $logger)
{
$this->campaign_type = $campaign;
$this->log = $logger;
}
public function save($data)
{
$this->campagin_type->save();
}
public function gets()
{
$this->campaign_type->gets();
}
public function log($data)
{
$this->log->insert_log($data);
}
public function get_campaign_details($campaign_id)
{
$this->log->get_details($campaign_id);
}
}
now Implementation code.
$campaign = new SmCampaignModel();
$logger = new SmsCampaignLogs();
$c = new Campaign($campagin,$logger);
$c->save($data);
$c->get($campaign_id);
$c->get_campaing_details();
Then I think if strategy pattern needed.
Simply, I can implement:
$campaign = new SmCampaignModel();
$logger = new SmsCampaignLogs();
$campaign->save($data);
$campaign->get($campaign_id);
$logger->get_campaing_details($campaign_id);
So I am now totally confused. I want your opinion on whether I applied SOLID principles correctly in my design (and strategy pattern is needed/used properly) or not.
In this case your Campaign class is only a Facade. No Strategy is in use.
You are actually using the Facades pattern and not the Strategy. Your Campaign class doesn't have a behavior of it own. It merely delegates its behavior to subsystem components. It is not a bad thing, but it makes your code somehow harder to maintain. It is fine in terms of information hiding.
There is not right or wrong in the aspect of OOD. Design patterns are not must to be included if no reason is presented. You should ask yourself: "What is my main problem, and does this solve it?". "Will there be a reason to the code to be changed often?".
Because we are all sometimes tempted to overuse design patterns, I would like to show you how making a simple OO relationship will do just fine, and will even be easier to read and maintain.
abstract class Campaign {
protected $ages;
protected $countries;
protected $dailyBudget;
protected $recipientsStatus = array(); // associative array or a composition of Recipients object
public function startCampaign()
{
// check there is not another run
$this->executeCampaign();
$this->collectRecipientsStatus();
$this->generateStatistics();
}
abstract protected function executeCampaign();
abstract protected function collectRecipientsStatus();
abstract protected function generateStatistics();
}
class EmailCampaign extends Campaign {
protected $addresses;
protected function executeCampaign()
{
$this->filterEmailsByCampaignData();
$this->sendEmails();
}
protected function filterEmailsByCampaignData()
{
// populate $this->addresses based on ages, countries etc.
}
protected function sendEmails()
{
// send email to addresses
}
protected function collectRecipientsStatus()
{
// collect status and fill parent $recipientsStatus
}
protected function generateStatistics()
{
// generate statistics
}
}
Campaign now is a class with data and behavior. We don't have to decouple it into components like Model and Logs. This will work just fine. However, if you ever find yourself with a bit more complicated Recipients array (too many key values or dimensional array code smells), then you might decouple it into another set of classes. But this should happen with evolution of the code. We simply cannot foresee everything in advance.
By the way, the only pattern I used is the lightweight Template Method, and inheritance feature of OOP.
I have a table called "User" that holds user info. I'll call my user class and create a user object so that I can grab whatever info is needed for that user.
I have been using the below classes and hybrids of them for my sites but have been never really happy with them mainly because I'm unsure how other developers handle this situation and if there is a better solution. Below are a few methods I have been using, I'm curious to see how other developers are doing.
Idea # 1
class User
{
//public user
public function __construct()
{
//get user - queries table and returns all fields in array
}
//get name function - references user array and returns name
//set name function - sets new name in array
//commit function - commits array to db (saves changes)
}
Idea # 2
class User
{
//user id
public function __construct(user id)
{
//set user id
}
//get name function - calls db directly and returns name
//set name function - calls db directly and sets name
//commit function - commits array to db saves changes
}
Idea #1 seems to be the most efficient, or least amount of DB calls but a little more complex and less straightforward than Idea #2.
I like idea #2 better but I'm afraid of it's scalability because each function makes a call to the DB.
Thoughts
I would recommend for you to learn about DataMapper pattern. The basic idea goes like this :
Lets say (for the sake of example ) that you have an MVC structure for application that deals with books.
It would make sense to have a model Library which is responsible with domain logic of managing different books.
The model deals with unknown number of Book instances ( many books in a library ). Each book :
knows everything about itself ( authors , publishing date , language .. etc.)
has no idea where it is stored or where the data come from
can be related to a table in Database , but contains information from multiple tables
And then you have an instance of BookMapper class, which :
model receives in the constructor ( implementing predefined interface )
knows how to ( and where ) store the Book objects , and how to read data into them
can be switched to a different object, if storage medium changes
if it works with DB, then it in constructor has already requested a DB object ( like PDO )
has methods store( Book $book ) and retrieve( Book $book ) for saving book's data , or getting new info from storage
This is how i would do it ..
What about something like:
class Db
{
function __construct()
{
$this->engine = DB_ENGINE;
$this->dbname = DB_NAME;
$this->host = DB_HOST;
$this->username = DB_USERNAME;
$this->password = DB_PASSWORD;
$this->connect();
}
function connect()
{
$this->db = new PDO($this->engine.':host='.$this->host.';dbname='.$this->dbname, $this->username, $this->password);
}
}
class Table extends Db
{
protected $from = null;
function __construct()
{
parent::__construct();
}
function select($columns, $where, $order, $offset, $limit)
{
}
function update($where, $data)
{
}
function delete($where)
{
}
etc...
}
class User extends Table
{
function __construct()
{
parent::__construct();
$this->from = 'blog';
}
function get_user()
{
$this->select(params);
}
function get_user_count()
{
}
etc...
}
This way you can easily use it to get other info as well just be creating a new class with it's functions to retrieve / delete / etc the info.
Firstly the user object is meant to hold user data, so i would suggest method #1, method #2 is more of a amodel object that send's commands to fetch data.
Now, you can automatically map user rows to an object in 1 single query, for example:
class User
{
//Predefine Here
public $id;
public $username;
public $password;
public $email;
public $hash;
public function profileLink()
{
return sprintf('%s',$this->id,$this->username);
}
}
//Fetch the results
$result = $sth->fetchAll(PDO::FETCH_CLASS, "User");
$userObjects = array();
foreach($result as $user)
{
$userObjects[] = $user;
}
//Now you can use like so:
echo $userObjects[2]->profileLink();
so my other answer here : PDO PHP Fetch Class
I think that if you are going to implement your own User class, then it should look something similar to the following:
class User {
private $UserID;
private $Username;
private $AvatarImg;
// ... First Name, Last Name, ALL other user information that you store in the DB.
function __construct( $uid ) {
// Populate all private members from the DB for the given user id.
}
function update( $assocArrayOfValues ) {
// Commit these values to the Db and Update the private members.
}
// All Associated get functions ( getName, getUID, getUsername etc )
// Any other specialty functions you may want, e.g...
function getAvatar() {
return "<img src = '" . $this->AvatarImg . "'/>";
}
}
What you are trying to archive is the functionally of an ORM (Object-Relational-Mapper). It may be beneficial to use one and not do it yourself.
If you want to do it on your own, I would go for lazy-loading. This is a bit in between your two ideas. In PHP it looks something like this:
class User
{
private $id; // depends on your application, could also be a string
private $dirty;
private $loaded;
private $properties;
public function __construct($user_id)
{
$this->id = $user_id;
}
public function __destruct()
{
if ($this->dirty) {
// commit to DB
}
}
public function __get($name)
{
if (!$loaded)
{
$this->loadFromDb();
}
// error-handling if the key does not exist is missing here!
return $this->properties[$name];
}
public function __set($name, $value)
{
// May be required to load the data here...
// error-handling if the key does not exist is missing here!
$properties[$name] = $value;
$this->dirty = true;
}
private function loadFromDb()
{
// query data from db and store it in properties
}
}
The advantage of this design is, that if you construct objects, which ultimately you don't need, nothing has touched the database yet. Also note the commit done during deconstruction.
If you load collections sometimes it may be useful to have a function load a bunch of rows from the DB and pass the rows as argument to a constructor when creating the objects. This would require only one query instead of possibly hundreds if you would have constructed each object by itself.
As a further enhancement you may provide a User::reset() function, which throws away all the changes made.
I would suggest you to use a PHP-Framework like Yii.
http://www.yiiframework.com/
It has nice Features to interact between Classes and your Database.
You can also get some inspiration there if you really want to do this on your own.
I'm wondering if anyone could give me a suggestion for to best handle this situation:
I have several systems from which to pull data to display on a single PHP-driven website. The type of information will be the same across systems (contacts, addresses, etc) but the way I pull data (MS-SQL, XML, REST) will not.
I want to create a class, or set of classes, for each of the connection types and use simple methods such as getContact(), getAddress(), etc. I am wondering how best to structure this.
The most obvious way that comes to mind means creating classes for each connection type, like:
class.sys_mysql.php. class.sys_xml.php, etc
But then won't I be duplicating the methods in each class? Maybe that's OK, but I'm curious if there's a better way, as far as future maintenance goes.
Maybe I should simply isolate the queries/data extraction methods, into separate class files? Classes within classes? Extended classes? I'm less familiar with these.
Any advice would be greatly appreciated.
DC
--------- more info ----------
Hi all. I really appreciate all the great advice. Not to belabor this thread but I'm still a bit confused on how I should break things down. I will try and be a bit more specific:
Basically, I have 3 (more in the future) offices, from which one PHP website pulls information. Each office uses a different CRM, and a different system for interfacing with that CRM. One uses MSSQL, another XML requests, etc.
Each office wants to display information similarly on the website, but there are minor differences. There may be more differences in the future. However, there are by far more similarities, and so I want to capitalize on higher level functions like getContacts($id) which are shared between them.
I am trying to write these classes so I can:
1) use higher level methods to pull data easily
2) account for different ways of pulling data (xml,sql,etc)
3) account for differences between how data is displayed on the website (office 1, office 2, office 3)
4) manage the connection credentials for each office and allow for expandability_
5) I should also mention that I will be creating separate classes for reporting, sending out automated e-mails, calculating finances...separate modules that will need to use existing classes to pull data.
I realize that some of the examples here see to cover 1 and 2, but I am confused as to how to get 3, 4 and 5 working with 1 and 2.
I really appreciate the help.
DC
This is what Interfaces are for.
You define the methods required to interact with the data in an Interface, and then you create classes that implement that Interface
If some of the systems have similar access models (i.e. perhaps two different DB Servers, but both are accessed using PDO) you could abstract it further and put the "low level" functionality into service-specific classes (which implement an Interface) and then a higher-level class which defines the actual methods you use.
Another option is that you could put the "common" methods (those that are identical or can be made idetntical with service-type checks) into a base class, which all others extend.
Example for option one:
interface DataModel {
public function findContacts($search);
public function getContact($id);
public function findAddresses($search);
public function getAddress($id);
}
class XMLDataModel implements DataModel {
public function findContacts($search) {
...
}
public function getContact($id) {
...
}
public function findAddresses($search) {
...
}
public function getAddress($id) {
...
}
}
class RESTDataModel implements DataModel {
public function findContacts($search) {
...
}
public function getContact($id) {
...
}
public function findAddresses($search) {
...
}
public function getAddress($id) {
...
}
}
As you can see, you simply define an Interface, which specifies which methods a class must implement.
If you had two very similar classes, perhaps one for MySQL and one for PostreSQL, and you can't/don't want to combine them into a single PDO class, you could do the following:
class PDODataModel implements DataModel {
private $model;
public function __construct ($serverType) {
if ($serverType === 'mysql') {
$this->model = new MySQLPDODataModel();
}
elseif ($serverType === 'postgresql') {
$this->model = new PostgresQLPDODataModel();
}
}
public function findContacts($search) {
// common logic about $search, perhaps checking it's a valid search?
$result = $this->model->searchForContacts($search);
// more common logic, maybe higher level filtering..
return $result;
}
public function getContact($id) {
...
}
public function findAddresses($search) {
...
}
public function getAddress($id) {
...
}
}
interface PDODataModelDriver {
public function searchForContacts($search);
}
class MySQLPDODataModel extends PDODataModel implements PDODataModelDriver {
public function searchForContacts($search) {
// MySQL-specific query to search for contacts
}
}
class PostgresSQLPDODataModel extends PDODataModel implements PDODataModelDriver {
public function searchForContacts($search) {
// PostgreSQL-specific query to search for contacts
}
}
The other option I mentioned was to work in the opposite direction:
abstract class PDODataModel implements DataModel {
protected $pdo;
protected $dsn;
public function __construct () {
$this->pdo = new PDO($this->dsn);
}
public function findContacts($search) {
// common logic about $search, perhaps checking it's a valid search?
$result = $this->searchForContacts($search);
// more common logic, maybe higher level filtering..
return $result;
}
public function getContact($id) {
...
}
public function findAddresses($search) {
...
}
public function getAddress($id) {
...
}
}
class MySQLPDODataModel extends PDODataModel {
protected $dsn = 'mysql:dbname=testdb;host=127.0.0.1';
protected function searchForContacts($search) {
// MySQL-specific query to search for contacts
}
}
class PostgresSQLPDODataModel extends PDODataModel {
protected $dsn = 'pgsql:host=localhost;port=5432;dbname=testdb';
protected function searchForContacts($search) {
// PostgreSQL-specific query to search for contacts
}
}
This is a classical example of a strategy design patter. Your first mind was absolutely fine, but if you're repeating yourself in each class you should consider creation of a abstract class that will handle the common code.
So it could look like this:
$myService = new MyService(new XMLReader('/path/to/file'));
echo $myService->getContanct('abc')->getName();
And skeleton of your classes:
class MyService {
private $reader;
public function __construct(ReaderInterface $reader) {
$this->reader = $reader;
}
// ...
public function getContacnt($id) {
$contact = $this->reader->getContact($id);
// do some extra stuff here
return $contact;
}
}
interface ReaderInterface {
public function getContanct($id);
public function getAddress($id);
}
abstract class AbstractReader implements ReaderInterface {
protected $loaded = false;
protected $data = array();
abstract protected function load();
public function getContanct($id) {
if ($this->loaded == false) {
$this->load();
$this->loaded = true;
}
return $this->data['contact'][$id];
}
}
class XMLReader extends AbstractReader {
public function __construct($filepath) {
...
}
protected function load() {
...
foreach (...) {
$this->data[...] = ...;
}
}
}
class MSSQLReader extends AbstractReader {
public function __construct(PDO $dbh) {
...
}
protected function load() {
...
while ($row = $stmt->fetchRow()) {
$this->data[...] = ...;
}
}
}
EDIT (2011-03-07) - According to your comment.
PHP supports variable variables (new $type()) but never use this! It's a horrible, and if overused make code really crappy.
This is a yet another example of a "classical issue". Use a factory pattern (depending on the complexion of the creation you might want to use more abstract variety of this pattern - abstract factory
When you need to dynamically determine class name (eg. from variable) use reflection API to instate an object.
You should create an object-storage mapping layer for each data source, which instantiates the objects into storage agnostic model objects. See http://martinfowler.com/eaaCatalog/dataMapper.html
If you have control over the structure of your data formats, I suggest you serialize your data in a consistent way (especially in XML) and provide drivers for each data format.
For instance, every driver will have 'findAll', 'getOne', 'count', etc. methods. The driver can be given a model to populate with the retrieved data.
abstract class DataDriver {
function __construct($model) {}
abstract public function findAll();
abstract public function getOne();
abstract public function count();
// ...
}
class XMLDriver extends DataDriver {
// implements all the methods
}
class SQLDriver extends DataDriver {
// implements all the methods
}
class Contact {
public var $firstName;
public var $lastName;
function getFullName() {
return trim($this->firstName . ' ' . $this->lastName);
}
}
$accessor = new SQLDriver('Contact');
$contacts = $accessor->findAll();
If your data will be serialized in an uncontrolled manner, the approach you suggest is the best. Just make sure to separate your models (e.g. Address book, Contact) from the method of retrieval (eg. get_address_book_xml, get_address_book_sql, etc.)
Of course there are many ways of separating your models from your data-mapping driver. The importance is you find the solution that works best for you given that you're using such different formats.
I have a PHP MVC application using Zend Framework. As presented in the quickstart, I use 3 layers for the model part :
Model (business logic)
Data mapper
Table data gateway (or data access object, i.e. one class per SQL table)
The model is UML designed and totally independent of the DB.
My problem is : I can't have multiple instances of the same "instance/record".
For example : if I get, for example, the user "Chuck Norris" with id=5, this will create a new model instance wich members will be filled by the data mapper (the data mapper query the table data gateway that query the DB). Then, if I change the name to "Duck Norras", don't save it in DB right away, and re-load the same user in another variable, I have "synchronisation" problems... (different instances for the same "record")
Right now, I use the Multiton / Identity Map pattern : like Singleton, but multiple instances indexed by a key (wich is the user ID in our example). But this is complicating my developpement a lot, and my testings too.
How to do it right ?
Identity Map
Edit
In response to this comment:
If I have a "select * from X", how can I skip getting the already loaded records ?
You can't in the query itself, but you can in the logic that loads the rows into entity objects. In pseudo-code:
class Person {}
class PersonMapper {
protected $identity_map = array();
function load($row) {
if (!isset($this->identity_map[$row['id']])) {
$person = new Person();
foreach ($row as $key => $value) {
$person->$key = $value;
}
$this->identity_map[$row['id']] = $person;
}
return $this->identity_map[$row['id']];
}
}
class MappingIterator {
function __construct($resultset, $mapper) {
$this->resultset = $resultset;
$this->mapper = $mapper;
}
function next() {
$row = next($this->resultset);
if ($row) {
return $this->mapper->load($row);
}
}
}
In practice, you'd probably want your MappingIterator to implement Iterator, but I skipped it for brevity.
Keep all loaded model instances in "live model pool". When you load/query a model, first check if it has been already loaded into pool (use primary key or similar concept). If so, return the object (or a reference) from pool. This way all your references point to the same object. My terminology may be incorrect but hopefully you get the idea. Basically the pool acts as a cache between business logic and database.
Multiton
Best option if you want to use a variety of singletons in your project.
<?php
abstract class FactoryAbstract {
protected static $instances = array();
public static function getInstance() {
$className = static::getClassName();
if (!(self::$instances[$className] instanceof $className)) {
self::$instances[$className] = new $className();
}
return self::$instances[$className];
}
public static function removeInstance() {
$className = static::getClassName();
if (array_key_exists($className, self::$instances)) {
unset(self::$instances[$className]);
}
}
final protected static function getClassName() {
return get_called_class();
}
protected function __construct() { }
final protected function __clone() { }
}
abstract class Factory extends FactoryAbstract {
final public static function getInstance() {
return parent::getInstance();
}
final public static function removeInstance() {
parent::removeInstance();
}
}
// using:
class FirstProduct extends Factory {
public $a = [];
}
class SecondProduct extends FirstProduct {
}
FirstProduct::getInstance()->a[] = 1;
SecondProduct::getInstance()->a[] = 2;
FirstProduct::getInstance()->a[] = 3;
SecondProduct::getInstance()->a[] = 4;
print_r(FirstProduct::getInstance()->a);
// array(1, 3)
print_r(SecondProduct::getInstance()->a);
// array(2, 4)
I'm working on creating a domain layer in Zend Framework that is separate from the data access layer. The Data Access Layer is composed to two main objects, a Table Data Gateway and a Row Data Gateway. As per Bill Karwin's reply to this earlier question I now have the following code for my domain Person object:
class Model_Row_Person
{
protected $_gateway;
public function __construct(Zend_Db_Table_Row $gateway)
{
$this->_gateway = $gateway;
}
public function login($userName, $password)
{
}
public function setPassword($password)
{
}
}
However, this only works with an individual row. I also need to create a domain object that can represent the entire table and (presumably) can be used to iterate through all of the Person's in the table and return the appropriate type of person (admin, buyer, etc) object for use. Basically, I envision something like the following:
class Model_Table_Person implements SeekableIterator, Countable, ArrayAccess
{
protected $_gateway;
public function __construct(Model_DbTable_Person $gateway)
{
$this->_gateway = $gateway;
}
public function current()
{
$current = $this->_gateway->fetchRow($this->_pointer);
return $this->_getUser($current);
}
private function _getUser(Zend_Db_Table_Row $current)
{
switch($current->userType)
{
case 'admin':
return new Model_Row_Administrator($current);
break;
case 'associate':
return new Model_Row_Associate($current);
break;
}
}
}
Is this is good/bad way to handle this particular problem? What improvements or adjustments should I make to the overall design?
Thanks in advance for your comments and criticisms.
I had in mind that you would use the Domain Model class to completely hide the fact that you're using a database table for persistence. So passing a Table object or a Row object should be completely under the covers:
<?php
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
$db = Zend_Db::factory('mysqli', array('dbname'=>'test',
'username'=>'root', 'password'=>'xxxx'));
Zend_Db_Table_Abstract::setDefaultAdapter($db);
class Table_Person extends Zend_Db_Table_Abstract
{
protected $_name = 'person';
}
class Model_Person
{
/** #var Zend_Db_Table */
protected static $table = null;
/** #var Zend_Db_Table_Row */
protected $person;
public static function init() {
if (self::$table == null) {
self::$table = new Table_Person();
}
}
protected static function factory(Zend_Db_Table_Row $personRow) {
$personClass = 'Model_Person_' . ucfirst($personRow->person_type);
return new $personClass($personRow);
}
public static function get($id) {
self::init();
$personRow = self::$table->find($id)->current();
return self::factory($personRow);
}
public static function getCollection() {
self::init();
$personRowset = self::$table->fetchAll();
$personArray = array();
foreach ($personRowset as $person) {
$personArray[] = self::factory($person);
}
return $personArray;
}
// protected constructor can only be called from this class, e.g. factory()
protected function __construct(Zend_Db_Table_Row $personRow) {
$this->person = $personRow;
}
public function login($password) {
if ($this->person->password_hash ==
hash('sha256', $this->person->password_salt . $password)) {
return true;
} else {
return false;
}
}
public function setPassword($newPassword) {
$this->person->password_hash = hash('sha256',
$this->person->password_salt . $newPassword);
$this->person->save();
}
}
class Model_Person_Admin extends Model_Person { }
class Model_Person_Associate extends Model_Person { }
$person = Model_Person::get(1);
print "Got object of type ".get_class($person)."\n";
$person->setPassword('potrzebie');
$people = Model_Person::getCollection();
print "Got ".count($people)." people objects:\n";
foreach ($people as $i => $person) {
print "\t$i: ".get_class($person)."\n";
}
"I thought static methods were bad
which is why I was trying to create
the table level methods as instance
methods."
I don't buy into any blanket statement that static is always bad, or singletons are always bad, or goto is always bad, or what have you. People who make such unequivocal statements are looking to oversimplify the issues. Use the language tools appropriately and they'll be good to you.
That said, there's often a tradeoff when you choose one language construct, it makes it easier to do some things while it's harder to do other things. People often point to static making it difficult to write unit test code, and also PHP has some annoying deficiencies related to static and subclassing. But there are also advantages, as we see in this code. You have to judge for yourself whether the advantages outweigh the disadvantages, on a case by case basis.
"Would Zend Framework support a Finder
class?"
I don't think that's necessary.
"Is there a particular reason that you
renamed the find method to be get in
the model class?"
I named the method get() just to be distinct from find(). The "getter" paradigm is associated with OO interfaces, while "finders" are traditionally associated with database stuff. We're trying to design the Domain Model to pretend there's no database involved.
"And would you use continue to use the
same logic to implement specific getBy
and getCollectionBy methods?"
I'd resist creating a generic getBy() method, because it's tempting to make it accept a generic SQL expression, and then pass it on to the data access objects verbatim. This couples the usage of our Domain Model to the underlying database representation.