i'm starting with Zend Framework and I'm a little bit confused with models and relathionships (one-to-many, many-to-many etc).
The "Zend Framework Quick Start" says to create a Zend_Db_Table, a Data Mapper and finally
our model class
Suppose we have a database like this:
table A (
id integer primary key,
name varchar(50)
);
table B (
id integer primary key,
a_id integer references A
);
then, i'll create:
Application_Model_DbTable_A extends Zend_Db_Table_Abstract,
Application_Model_AMapper,
Application_Model_A,
Application_Model_DbTable_B extends Zend_Db_Table_Abstract,
Application_Model_BMapper,
Application_Model_B,
if I understood, i've to store the references informations in
Application_Model_DbTable_A:
protected $_dependentTables = array('B');
and Application_Model_DbTable_B:
protected $_referenceMap = array(
'A' => array(
'columns' => array('a_id'),
'refTableClass' => 'A',
'refColums' => array('id')
)
);
and my models class:
class Application_Model_A
{
protected $_id;
protected $_name;
public function __construct(array $options = null)
{
if(is_array($options)) {
$this->setOptions($options);
}
}
public function __set($name, $value)
{
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid property');
}
$this->$method($value);
}
public function __get($name)
{
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid 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;
}
public function setName($name)
{
$this->_name = (string) $name;
return $this;
}
public function getName()
{
return $this->_name;
}
public function setId($id)
{
$this->_id = (int) $id;
return $this;
}
public function getId()
{
return $this->_id;
}
class Application_Model_B
{
protected $_id;
protected $_a_id;
public function __construct(array $options = null)
{
if(is_array($options)) {
$this->setOptions($options);
}
}
public function __set($name, $value)
{
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid property');
}
$this->$method($value);
}
public function __get($name)
{
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid 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;
}
public function setA_id($a_id)
{
$this->_a_id = (int) $a_id;
return $this;
}
public function getA_id()
{
return $this->_a_id;
}
public function setId($id)
{
$this->_id = (int) $id;
return $this;
}
public function getId()
{
return $this->_id;
}
it's that right?
Well let's assume you have a db table called products. You want to access this table and work with the data.
First you need in models/dbtable/ folder a Product.php file to tell zf where to look for the data:
class Default_Model_DbTable_Product extends Zend_Db_Table_Abstract
{
protected $_name = 'products';
}
So $_name is the name of the db table. Second you need a class where you store all the information like product attributes. Let's say a product has just a name:
class Default_Model_Product{
protected $_name;
public function setName($name) {
$this->_name = $name;
return $this;
}
public function getName() {
return $this->_name;
}
public function __construct(array $options = null) {
if (is_array ( $options )) {
$this->setOptions ( $options );
}
}
//you need the methods below to access an instanz of this class
//put it in every model class
public function __set($name, $value) {
$method = 'set' . $name;
if (('mapper' == $name) || ! method_exists ( $this, $method )) {
throw new Exception ( 'Invalid product property' );
}
$this->$method ( $value );
}
public function __get($name) {
$method = 'get' . $name;
if (('mapper' == $name) || ! method_exists ( $this, $method )) {
throw new Exception ( 'Invalid product 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;
}
}
Finally you need a mapper class where you actually access the data. You retrieve the data from the table and store it as objects in an array:
class Default_Model_ProductMapper {
protected $_dbTable;
public function setDbTable($dbTable) {
if (is_string ( $dbTable )) {
$dbTable = new $dbTable ();
}
if (! $dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception ( 'Invalid table data gateway provided' );
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable() {
if (null === $this->_dbTable) {
$this->setDbTable ( 'Default_Model_DbTable_Product' );
}
return $this->_dbTable;
}
//e.g. fetch all
public function fetchAll() {
$resultSet = $this->getDbTable ()->fetchAll ();
$entries = array ();
foreach ( $resultSet as $row ) {
$entry = new Default_Model_Product ();
$entry->setName ( $row->name );
$entries [] = $entry;
}
return $entries;
}
}
This is really straight forward ;)
I really recommend you to use another DB-Framework in your MVC-System.
Zend isn't quite nice with Databases and it's IMO too complicated/ too much code to write.
There are Frameworks which play very nice with Zend (VC) like PHPActiveRecord...
--> http://www.phpactiverecord.org/projects/main/wiki/Frameworks
Related
Consider the following to classes:
class objectCollection implements Iterator {
private $objectArray = [];
private $position = 0;
public function __construct() {
$this->position = 0;
}
public function add( $object ){
if(!in_array( $object, $this->objectArray))
$this->objectArray[] = $object;
return $this;
}
public function remove( $object ){
if(($key = array_search( $object, $this->objectArray())) !== false)
unset($this->objectArray[$key]);
return $this;
}
public function rewind() {
$this->position = 0;
}
public function current() {
return $this->objectArray[$this->position];
}
public function key() {
return $this->position;
}
public function next() {
++$this->position;
}
public function valid() {
return isset($this->objectArray[$this->position]);
}
}
class attachmentCollection extends objectCollection {
public function add( attachment $attachment ){
return parent::add($attachment);
}
public function remove( attachment $attachment ){
return parent::remove($attachment);
}
}
This produces the follwing error:
Declaration of attachmentCollection::add() should be compatible with objectCollection::add($object)
When you look at the code, I think it's rather obvious what I am trying to do.
I want the attachmentCollection to essentially be the same as the objectCollection, except that the objects that can be added ( or removed ) need to be an instance of attachment.
What is the right way to do this ?
PHP errored code
PHP working code
Change this to
public function add( $object ){
if(!in_array( $object, $this->objectArray))
$this->objectArray[] = $object;
return $this;
}
public function remove( $object ){
if(($key = array_search( $object, $this->objectArray())) !== false)
unset($this->objectArray[$key]);
return $this;
}
This
public function add(attachment $object ){
if(!in_array( $object, $this->objectArray))
$this->objectArray[] = $object;
return $this;
}
public function remove(attachment $object ){
if(($key = array_search( $object, $this->objectArray())) !== false)
unset($this->objectArray[$key]);
return $this;
}
If nobody has a better solution, here is what I went with ( still open to a better solution ):
class attachmentCollection extends objectCollection {
public function add( $attachment ){
if(! $attachment instanceof attachment) throw new \Exception('Argument must be instance of attachment');
return parent::add($attachment);
}
public function remove( $attachment ){
if(! $attachment instanceof attachment) throw new \Exception('Argument must be instance of attachment');
return parent::remove($attachment);
}
}
in the case you want to extend further classes from the objectClass and the methods should be compatible, then i would recommend to use an Interface as the passed parameter, for example:
interface collection{
//your methods
}
class objectCollection implements Iterator {
private $objectArray = [];
private $position = 0;
public function add(collection $object){
if(!in_array( $object, $this->objectArray))
$this->objectArray[] = $object;
return $this;
}
public function remove(collection $object ){
if(($key = array_search( $object, $this->objectArray())) !==false)
unset($this->objectArray[$key]);
return $this;
}
....
}
class attachmentCollection extends objectCollection {
public function add(collection $attachment ){
return parent::add($attachment);
}
public function remove( collection $attachment ){
return parent::remove($attachment);
}
}
class productCollection extends objectCollection {
public function add(collection $attachment ){
return parent::add($attachment);
}
public function remove(collection $attachment ){
return parent::remove($attachment);
}
}
class attachment implements collection {
//implement interface methods
}
class product implements collection{
//implement interface methods
}
I tried to set up a sign up logic but suffer a problem said
Message: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'member_login' cannot be null, query was: INSERT INTO members (member_login) VALUES (?)enter image description here
After struggling for hours, still no ideas which go wrong. Here is my source code.
Anyone can give me some ideas?
My Model.php
<?php
class Application_Model_Member
{
protected $_id;
protected $_member_login;
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
}
}
public function __set($name, $value)
{
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid member property');
}
$this->$method($value);
}
public function __get($name)
{
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid member 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;
}
public function setId($id)
{
$this->_id = (int) $id;
return $this;
}
public function getId()
{
return $this->_id;
}
public function setMemberLogin($text)
{
$this->_member_login = (string) $text;
return $this;
}
public function getMemberLogin()
{
return $this->_member_login;
}
}
My MemberMapper.php
<?php
class Application_Model_MemberMapper
{
protected $_dbTable;
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Application_Model_DbTable_Members');
}
return $this->_dbTable;
}
public function save(Application_Model_Member $member)
{
$data = array(
'member_login' => $member->getMemberLogin(),
);
if (null === ($id = $member->getId())) {
unset($data['member_id']);
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array('member_id = ?' => $id));
}
}
public function find($id, Application_Model_Member $member)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
return;
}
$row = $result->current();
$member->setId($row->member_id)
->setMemberLogin($row->member_login);
}
public function fetchAll()
{
$resultSet = $this->getDbTable()->fetchAll();
$entries = array();
foreach ($resultSet as $row) {
$entry = new Application_Model_Member();
$entry->setId($row->member_id)
->setMemberLogin($row->member_login);
$entries[] = $entry;
}
return $entries;
}
}
DbTable:
class Application_Model_DbTable_Members extends Zend_Db_Table_Abstract
{
protected $_name = 'members';
}
Form: Registration.php
<?php
class Application_Form_Auth_Registration extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$this->addElement(
'text', 'member_login', array(
'label' => 'Username:',
'required' => true,
'filters' => array('StringTrim')
));
$this->addElement('submit', 'register', array(
'ignore' => true,
'label' => 'Sign up'
));
}
}
Signup controller:
public function signupAction()
{
$request = $this->getRequest();
$regform = new Application_Form_Auth_Registration();
if ($this->getRequest()->isPost()) {
if ($regform->isValid($request->getPost())) {
$member = new Application_Model_Member($regform->getValues());
$mapper = new Application_Model_MemberMapper();
$mapper->save($member);
return $this->_helper->redirector('/books/view');
}
}
$this->view->regform = $regform;
}
Finally I fix the bug. It go wrong with the naming of Element. For example, in your database you have "member_login", then the Element name should be sth like memberLogin.
I'm from the C# environment and I'm starting to learn PHP in school.
I'm used to set my properties in C# like this.
public int ID { get; set; }
What's the equivalent to this in php?
Thanks.
There is none, although there are some proposals for implementing that in future versions.
For now you unfortunately need to declare all getters and setters by hand.
private $ID;
public function setID($ID) {
$this->ID = $ID;
}
public function getID() {
return $this->ID;
}
for some magic (PHP likes magic), you can look up __set and __get magic methods.
Example
class MyClass {
private $ID;
private function setID($ID) {
$this->ID = $ID;
}
private function getID() {
return $this->ID;
}
public function __set($name,$value) {
switch($name) { //this is kind of silly example, bt shows the idea
case 'ID':
return $this->setID($value);
}
}
public function __get($name) {
switch($name) {
case 'ID':
return $this->getID();
}
}
}
$object = new MyClass();
$object->ID = 'foo'; //setID('foo') will be called
Thanks for your answers everyone. It helped me to create something like this:
In my parent class:
public function __get($name){
if (ObjectHelper::existsMethod($this,$name)){
return $this->$name();
}
return null;
}
public function __set($name, $value){
if (ObjectHelper::existsMethod($this,$name))
$this->$name($value);
}
ObjectHelper::existsMethod is a method which just check if given protected method exists.
private $_propertyName = null;
protected function PropertyName($value = ""){
if (empty($value)) // getter
{
if ($this-> _propertyName != null)
return $this->_propertyName;
}
else // setter
{
$this-> _propertyName = $value;
}
return null;
}
So I can use something like this in any class:
$class = new Class();
$class->PropertyName = "test";
echo $class->PropertyName;
I was inspired by C# :)
What do you think about this, guys?
Here is my ObjectHelper if someone would like to use it:
namespace Helpers;
use ReflectionMethod;
class ObjectHelper {
public static function existsMethod($obj, $methodName){
$methods = self::getMethods($obj);
$neededObject = array_filter(
$methods,
function ($e) use($methodName) {
return $e->Name == $methodName;
}
);
if (is_array($neededObject))
return true;
return false;
}
public static function getMethods($obj){
$var = new \ReflectionClass($obj);
return $var->getMethods(ReflectionMethod::IS_PROTECTED);
}
}
Mchi is right, but there is another way of doing it by using single function
private $ID;
public function ID( $value = "" )
{
if( empty( $value ) )
return $this->ID;
else
$this->ID = $value;
}
But yeah this approach is pretty much inline with what you do in c#. but this is only an alternative
Or try using php's __set and __get in your class more info here
http://php.net/manual/en/language.oop5.overloading.php
Another exampled using Variable function name
class MyClass {
private $ID;
protected $ID2;
private function setID($ID) {
$this->ID = $ID;
}
private function getID() {
return $this->ID;
}
private function setID2($ID2) {
$this->ID2 = $ID2;
}
private function getID2() {
return $this->ID2;
}
public function __set($name,$value) {
$functionname='set'.$name;
return $this->$functionname($value);
}
public function __get($name) {
$functionname='get'.$name;
return $this->$functionname();
}
}
$object = new MyClass();
$object->ID = 'foo'; //setID('foo') will be called
$object->ID2 = 'bar'; //setID2('bar') will be called
private $ID;
public function getsetID($value = NULL)
{
if ($value === NULL) {
return $this->ID;
} else {
$this->ID = $value;
}
}
I know I am a bit late to the party on this question, but I had the same question/thought myself. As a C# developer who does PHP, when the job requires, I want to have a simple way to create properties just I would be able to in C#.
I whipped up a first draft this afternoon which allows you to create the backing fields and specify their accessors or have pure accessors with no backing field. I will update my answer as the code evolves and provide a link when I get it to the state where it can be imported as a composer package.
For simplicity, I created the functionality as a PHP trait so you can drop it in to any class you want instead of having to extend a base class. Eventually I hope to extend this functionality to discern between external public calls to the properties and protected/private calls.
Here is the code for the trait itself:
trait PropertyAccessorTrait
{
private static $__propertyAccessors = [];
/* #property string $__propertyPrefix */
public function __get($name)
{
$this->__populatePropertyAcessors($name);
return $this->__performGet($name);
}
public function __set($name, $value)
{
$this->__populatePropertyAcessors($name);
$this->__performSet($name, $value);
}
public function __isset($name)
{
// TODO: Implement __isset() method.
}
public function __unset($name)
{
// TODO: Implement __unset() method.
}
protected function __getBackingFieldName($name)
{
if (property_exists(self::class, '__propertyPrefix')) {
$prefix = $this->__propertyPrefix;
} else {
$prefix = '';
}
return $prefix . $name;
}
protected function __canget($name)
{
$accessors = $this->__getPropertyAccessors($name);
return $accessors !== null && isset($accessors['get']);
}
protected function __canset($name)
{
$accessors = $this->__getPropertyAccessors($name);
return $accessors !== null && isset($accessors['set']);
}
protected function __performGet($name)
{
if (!$this->__canget($name)) {
throw new \Exception('Getter not allowed for property: ' . $name);
}
$accessors = $this->__getPropertyAccessors($name)['get'];
/* #var \ReflectionMethod $method */
$method = $accessors['method'];
if (!empty($method)) {
return $method->invoke($this);
}
return $this->{$this->__getBackingFieldName($name)};
}
protected function __performSet($name, $value)
{
if (!$this->__canset($name)) {
throw new \Exception('Setter not allowed for property: ' . $name);
}
$accessors = $this->__getPropertyAccessors($name)['set'];
/* #var \ReflectionMethod $method */
$method = $accessors['method'];
if (!empty($method)) {
return $method->invoke($this, $value);
}
$this->{$this->__getBackingFieldName($name)} = $value;
}
protected function __getPropertyAccessors($name)
{
return isset(self::$__propertyAccessors[$name])
? self::$__propertyAccessors[$name]
: null
;
}
protected function __getAccessorsFromDocBlock($docblock)
{
$accessors = [];
if (!empty(trim($docblock))) {
$doclines = null;
if (!empty($docblock)) {
$doclines = explode("\n", $docblock);
}
if (!empty($doclines)) {
foreach ($doclines as $line) {
if (preg_match('/#(get|set)\\s+(public|private|protected)/', $line, $matches)) {
$accessors[$matches[1]]['visibility'] = $matches[2];
}
}
}
}
return $accessors;
}
protected function __populatePropertyAcessors($name)
{
if ($this->__getPropertyAccessors($name) !== null) return;
try {
$property = new \ReflectionProperty(self::class, $this->__getBackingFieldName($name));
} catch (\ReflectionException $ex) {
$property = null;
}
$accessors = [];
if ($property != null) {
$accessors = $this->__getAccessorsFromDocBlock($property->getDocComment());
}
try {
$methodName = 'get' . ucfirst($name);
$method = new \ReflectionMethod(self::class, $methodName);
$method->setAccessible(true);
$accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment()));
} catch (\ReflectionException $ex) {
$method = null;
}
if ($method !== null || isset($accessors['get'])) {
$accessors['get']['method'] = $method;
}
try {
$methodName = 'set' . ucfirst($name);
$method = new \ReflectionMethod(self::class, $methodName);
$method->setAccessible(true);
$accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment()));
} catch (\ReflectionException $ex) {
$method = null;
}
if ($method !== null || isset($accessors['set'])) {
$accessors['set']['method'] = $method;
}
self::$__propertyAccessors[$name] = $accessors;
}
}
Here is a quick unit test I created using the Codeception format:
<?php
class PropertyAssesorTraitTestClass
{
use PropertyAccessorTrait;
private $__propertyPrefix = '_';
/**
* #get public
* #set public
*/
private $_integer = 1;
/**
* #get public
*/
private $_getonly = 100;
/**
* #set public
*/
private $_setonly;
private $_customDoubler;
private function getCustomDoubler()
{
return $this->_customDoubler * 2;
}
private function setCustomDoubler($value)
{
$this->_customDoubler = $value * 2;
}
public $publicField = 1234;
/**
* #return int
* #get public
*/
private function getPureAccessor()
{
return $this->publicField;
}
/**
* #param $value
* #set public
*/
private function setPureAccessor($value)
{
$this->publicField = $value;
}
private $_purePrivate = 256;
}
$I = new UnitTester($scenario);
$I->wantTo('Ensure properties are accessed correctly');
$instance = new PropertyAssesorTraitTestClass();
$I->assertSame(1, $instance->integer);
$instance->integer = 2;
$I->assertSame(2, $instance->integer);
$instance->integer = $instance->integer + 1;
$I->assertSame(3, $instance->integer);
$instance->integer++;
$I->assertSame(4, $instance->integer);
$I->assertSame(100, $instance->getonly);
$I->expectException('Exception', function () use ($instance) { $instance->getonly = 50; });
$instance->setonly = 50;
$I->expectException('Exception', function () use ($instance) { $a = $instance->setonly; });
$instance->customDoubler = 100;
$I->assertSame(400, $instance->customDoubler);
$I->assertSame(1234, $instance->publicField);
$instance->pureAccessor = 1000;
$I->assertSame(1000, $instance->publicField);
$instance->publicField = 1234;
$I->assertSame(1234, $instance->publicField);
$I->assertSame(1234, $instance->pureAccessor);
$I->expectException('Exception', function () use ($instance) { return $instance->purePrivate; });
I like to use this pattern:
class foo
{
//just add p as prefix to be different than method name.
protected $pData;
public funtion __construct() {}
public funtion __destruct() {}
public funtion __clone() {}
public function Data($value == "")
{
if ($value != "") {
$this->pData = $value;
}
return $this->pData;
}
}
$myVar = new foo();
//for SET
$myVar->Data("A Value");
//for GET
$item = $myVar->Data();
class MyClass
{
private $name = null;
public function __construct($name = null)
{
$this->name = $name;
}
public function __set($name, $value)
{
if (property_exists($this, $name)) {
$this->name = $value;
}
return $this;
}
public function __get($name)
{
if (property_exists($this, $name)) {
return $this->$name;
}
return null;
}
}
this is PHP ; you don't need get set
class MyClass {
public $ID;
}
$object = new MyClass();
$object->ID = 'foo';
echo $object->ID;
will work
I have a problem with implementing Iterator interface
Here is the code:
class User_Model_Users implements Iterator, Countable
{
protected $_count;
protected $_gateway;
protected $_resultSet;
public function __construct($results, $gateway)
{
$this->setGateway($gateway);
$this->_resultSet = $results;
}
public function setGateway(User_Model_UserGateway $gateway)
{
$this->_gateway = $gateway;
return $this;
}
public function getGateway()
{
return $this->_gateway;
}
public function count()
{
if (null === $this->_count) {
$this->_count = count($this->_resultSet);
}
return $this->_count;
}
public function current()
{
if ($this->_resultSet instanceof Iterator) {
$key = $this->_resultSet->key();
} else {
$key = key($this->_resultSet);
}
$result = $this->_resultSet [$key];
if (!$result instanceof User_Model_User) {
$gateway = $this->getGateway();
$result = $gateway->createUser($result);
$this->_resultSet [$key] = $result;
}
return $result;
}
public function key()
{
return key($this->_resultSet);
}
public function next()
{
return next($this->_resultSet);
}
public function rewind()
{
return reset($this->_resultSet);
}
public function valid()
{
return (bool) $this->current();
}
}
As a $result I provide Zend_Db_Table_Rowset but it can be also other object or array.
How can I fix this code so that I works in a foreach loop?
I don't get any errors as it is an infinite loop.
Wild guess (haven't really delved into the code):
Since your implementation of valid() is
public function valid()
{
return (bool) $this->current();
}
you should make sure that current() returns false when there are no more elements
public function current()
{
if ($this->_resultSet instanceof Iterator) {
$key = $this->_resultSet->key();
} else {
$key = key($this->_resultSet);
}
if ( is_null($key) ) { // could also be is_null($key)||false===$key, not sure...
return false;
}
$result = $this->_resultSet[$key];
if (!$result instanceof User_Model_User) {
$gateway = $this->getGateway();
$result = $gateway->createUser($result);
$this->_resultSet[$key] = $result;
}
return $result;
}
and btw: your function key() doesn't implement the instanceof Iterator case like current() does.
Test script:
<?php
class User_Model_Users implements Iterator, Countable
{
protected $_count;
protected $_gateway;
protected $_resultSet;
public function __construct($results, $gateway)
{
$this->setGateway($gateway);
$this->_resultSet = $results;
$this->_count = null;
}
public function setGateway(User_Model_UserGateway $gateway)
{
$this->_gateway = $gateway;
return $this;
}
public function getGateway()
{
return $this->_gateway;
}
public function count()
{
if ( is_null($this->_count) ) {
$this->_count = count($this->_resultSet);
}
return $this->_count;
}
public function current()
{
if ($this->_resultSet instanceof Iterator) {
$key = $this->_resultSet->key();
} else {
$key = key($this->_resultSet);
}
if ( is_null($key) ) {
return false;
}
$result = $this->_resultSet[$key];
if (!$result instanceof User_Model_User) {
$gateway = $this->getGateway();
$result = $gateway->createUser($result);
$this->_resultSet[$key] = $result;
}
return $result;
}
public function key()
{
return key($this->_resultSet);
}
public function next()
{
return next($this->_resultSet);
}
public function rewind()
{
return reset($this->_resultSet);
}
public function valid()
{
return (bool) $this->current();
}
}
class User_Model_User {
protected $x, $y;
public function __construct($x, $y) {
$this->x = $x;
$this->y = $y;
}
}
class User_Model_UserGateway {
protected $id;
public function __construct() {
$this->id = time();
}
public function createUser($x) {
echo "User_Model_UserGateway::createUser($x)\n";
return new User_Model_User($x, $this->id);
}
}
$a = array('a', 'b', 'c', 'd');
$users = new User_Model_Users($a, new User_Model_UserGateway);
foreach($users as $u) {
print_r($u);
}
prints
User_Model_UserGateway::createUser(a)
User_Model_User Object
(
[x:protected] => a
[y:protected] => 1311167311
)
User_Model_UserGateway::createUser(b)
User_Model_User Object
(
[x:protected] => b
[y:protected] => 1311167311
)
User_Model_UserGateway::createUser(c)
User_Model_User Object
(
[x:protected] => c
[y:protected] => 1311167311
)
User_Model_UserGateway::createUser(d)
User_Model_User Object
(
[x:protected] => d
[y:protected] => 1311167311
)
class Contact{
public $name;
public $bgcolor;
public $lgcolor;
public $email;
public $element;
public function __construct($name, $bgcolor, $lgcolor, $email, $element)
{
$this->name = $name;
$this->bgcolor = $bgcolor;
$this->lgcolor = $lgcolor;
$this->email = $email;
$this->element = $element;
}
public static function sortByName(Contact $p1, Contact $p2)
{
return strcmp($p1->name, $p2->name);
}
}
class ContactList implements Iterator, ArrayAccess
{
protected $_label;
protected $_contacts = array();
public function __construct($label)
{
$this->_label = $label;
}
public function getLabel()
{
return $this->_label;
}
public function addContact(Contact $contact)
{
$this->_contacts[] = $contact;
}
public function current()
{
return current($this->_contacts);
}
public function key()
{
return key($this->_contacts);
}
public function next()
{
return next($this->_contacts);
}
public function rewind()
{
return reset($this->_contacts);
}
public function valid()
{
return current($this->_contacts);
}
public function offsetGet($offset)
{
return $this->_contacts[$offset];
}
public function offsetSet($offset, $data)
{
if (!$data instanceof Contact)
throw new InvalidArgumentException('Only Contact objects allowed in a ContactList');
if ($offset == '')
{
$this->_contacts[] = $data;
} else
{
$this->_contacts[$offset] = $data;
}
}
public function offsetUnset($offset)
{
unset($this->_contacts[$offset]);
}
public function offsetExists($offset) {
return isset($this->_contacts[$offset]);
}
public function sort($attribute = 'name')
{
$sortFct = 'sortBy' . ucfirst(strtolower($attribute));
if (!in_array($sortFct, get_class_methods('Contact')))
{
throw new Exception('contact->sort(): Can\'t sort by ' . $attribute);
}
usort($this->contact, 'ContactList::' . $sortFct);
}
}
public function Sort($property, $asc=true)
{
// this is where sorting logic takes place
$_pd = $this->_contact->getProperty($property);
if ($_pd == null)
{
user_error('Property '.$property.' does not exist in class '.$this->_contact->getName(), E_WARNING);
return;
}
// set sortDescriptor
ContactList::$sortProperty = $_pd;
// and apply sorting
usort($this->_array, array('ContactList', ($asc?'USortAsc':'USortDesc')));
}
function getItems(){
return $this->_array;
}
class SortableItem extends ContactList
{
static public $sortProperty;
static function USortAsc($a, $b)
{
/*#var $_pd ReflectionProperty*/
/*
$_pd = self::$sortProperty;
if ($_pd !== null)
{
if ($_pd->getValue($a) === $_pd->getValue($b))
return 0;
else
return (($_pd->getValue($a) < $_pd->getValue($b))?-1:1);
}
return 0;
}
static function USortDesc($a, $b)
{
return -(self::USortAsc($a,$b));
}
}
This approach keeps giving me PHP Warnings: usort() [function.usort]: of all kinds which I can provide later as needed to comment out those methods and definitions in order to test and fix some minor bugs of our program.
**$billy parameters are already defined.
$all -> addContact($billy);
// --> ended up adding each contact manually above
$all->Sort('name',true);
$items = $all->getItems();
foreach($items as $contact)
{
echo $contact->__toString();
}
$all->sort();
The reason for using usort is to re-arrange the order alphabetically by name but somehow is either stating that the function comparison needs to be an array or another errors which obviously I have seemed to pass. Any help would be greatly appreciated, thanks in advance.
It's happening because the variable inside the usort call is not a valid array. You use $this->_contacts everywhere, but your usort line is:
usort($this->contact, 'ContactList::' . $sortFct);
Try changing that to:
usort($this->_contacts, 'ContactList::' . $sortFct);
<?php
class Contact{
public $name;
public $bgcolor;
public $lgcolor;
public $email;
public $element;
public function __construct($name, $bgcolor, $lgcolor, $email, $element)
{
$this->name = $name;
$this->bgcolor = $bgcolor;
$this->lgcolor = $lgcolor;
$this->email = $email;
$this->element = $element;
}
}
class ContactList implements Iterator, ArrayAccess
{
public $_label;
public $_contacts = array();
public function __construct($label)
{
$this->_label = $label;
}
public function getLabel()
{
return $this->_label;
}
public function addContact(Contact $contact)
{
$this->_contacts[] = $contact;
}
public function current()
{
return current($this->_contacts);
}
public function key()
{
return key($this->_contacts);
}
public function next()
{
return next($this->_contacts);
}
public function rewind()
{
return reset($this->_contacts);
}
public function valid()
{
return current($this->_contacts);
}
public function offsetGet($offset)
{
return $this->_contacts[$offset];
}
public function offsetSet($offset, $data)
{
if (!$data instanceof Contact)
throw new InvalidArgumentException('Only Contact objects allowed in a ContactList');
if ($offset == '')
{
$this->_contacts[] = $data;
} else
{
$this->_contacts[$offset] = $data;
}
}
public function offsetUnset($offset)
{
unset($this->_contacts[$offset]);
}
public function offsetExists($offset) {
return isset($this->_contacts[$offset]);
}
/* This is the comparing function to be used with usort to make it alphabetically ordered for All Contacts */
public function sort_by($field, &$arr, $sorting='SORT_DSC', $case_insensitive=true){
if(is_array($arr) && (count($arr)>0) && ( ( is_array($arr[0]) && isset($arr[0][$field]) ) || ( is_object($arr[0]) && isset($arr[0]->$field) ) ) ){
if($case_insensitive==true) $strcmp_fn = "strnatcasecmp";
else $strcmp_fn = "strnatcmp";
if($sorting=='SORT_DSC'){
$fn = create_function('$a,$b', '
if(is_object($a) && is_object($b)){
return '.$strcmp_fn.'($a->'.$field.', $b->'.$field.');
}else if(is_array($a) && is_array($b)){
return '.$strcmp_fn.'($a["'.$field.'"], $b["'.$field.'"]);
}else return 0;
');
}else if($sorting=='SORT_ASC'){
$fn = create_function('$a,$b', '
if(is_object($a) && is_object($b)){
return '.$strcmp_fn.'($b->'.$field.', $a->'.$field.');
}else if(is_array($a) && is_array($b)){
return '.$strcmp_fn.'($b["'.$field.'"], $a["'.$field.'"]);
}else return 0;
');
}
usort($arr, $fn);
return true;
}else{
return false;
}
}
}
?
The call:
$all->sort_by('name',$all->_contacts,'SORT_DSC','false');