How to match model with the good brand? - php

i'm trying to make fake data with faker on symfony.
I have 2 entities Brand and Model with OneToMany relation :
/**
* #ORM\OneToMany(targetEntity=Model::class, mappedBy="brand")
*/
private $models;
/**
* #ORM\ManyToOne(targetEntity=Brand::class, inversedBy="models")
* #ORM\JoinColumn(nullable=false)
*/
private $brand;
The goal is to match the good brand with a specific model.
For example, when model it's 911 or 356 brand has to be Porsche.
Here my fixture :
public function load(ObjectManager $manager): void
{
$faker = Factory::create('fr_FR');
$brands = ['Porsche', 'Chevrolet', 'Aston Martin', 'Ferrari'];
foreach ($brands as $brand) {
$newBrand = new Brand();
$newBrand->setName($brand);
$manager->persist($newBrand);
$models = [911, 356, 'Corvette', 'Camaro', 'V8', 'DB4', 328, 'F430'];
foreach ($models as $model) {
$newModel = new Model();
if ($model == 911 || $model == 356) {
$newModel->setBrand($newBrand);
} elseif ($model == 'Corvette' || $model == 'Camaro') {
$newModel->setBrand();
} elseif ($model == 'V8' || $model == 'DB4') {
$newModel->setBrand();
} else {
$newModel->setBrand();
}
$newModel->setTransmission($faker->randomElement(['manual', 'automatic']))
->setName($model);
for ($i=1; $i<=6; $i++) {
$newModel->setImage("model$i.jpg");
}
$newModel->setColor($faker->randomElement(['red', 'black', 'yellow', 'blue']))
->setFuelType($faker->randomElement(['diesel fuel', 'gasoline', 'ethanol']))
->setPrice($faker->numberBetween($min=20000, $max=150000));
$manager->persist($newModel);
}
}
$manager->flush();
}

I simplified my code to achieve my goal, here is my function :
public function load(ObjectManager $manager): void
{
$faker = Factory::create('fr_FR');
$brands = ['Porsche', 'Chevrolet', 'Aston Martin', 'Ferrari'];
$brand1 = new Brand();
$brand1->setName('Porsche');
$manager->persist($brand1);
$brand2 = new Brand();
$brand2->setName('Chevrolet');
$manager->persist($brand2);
$brand3 = new Brand();
$brand3->setName('Aston Martin');
$manager->persist($brand3);
$brand4 = new Brand();
$brand4->setName('Ferrari');
$manager->persist($brand4);
$models = [911, 356, 'Corvette', 'Camaro', 'V8', 'DB4', 328, 'F430'];
foreach ($models as $model) {
$newModel = new Model();
if ($model == 911 || $model == 356) {
$newModel->setBrand($brand1);
} elseif ($model == 'Corvette' || $model == 'Camaro') {
$newModel->setBrand($brand2);
} elseif ($model == 'V8' || $model == 'DB4') {
$newModel->setBrand($brand3);
} else {
$newModel->setBrand($brand4);
}
$newModel->setTransmission($faker->randomElement(['manual', 'automatic']))
->setName($model);
for ($i=1; $i<=6; $i++) {
$newModel->setImage("model$i.jpg");
}
$newModel->setColor($faker->randomElement(['red', 'black', 'yellow', 'blue']))
->setFuelType($faker->randomElement(['diesel fuel', 'gasoline', 'ethanol']))
->setPrice($faker->numberBetween($min=20000, $max=150000));
$manager->persist($newModel);
}

Related

"Division by zero"/ NULL values TF-IDF

I'm trying to test tf-idf package from PHP-ML, I tried using their documentation code but it keeps giving me "Division by zero" when I try to use different samples(strings).
use Phpml\FeatureExtraction\TfIdfTransformer;
$samples = [
["Tareq", "Tareq", "Tareq"],
["Mohammad", "Ahmad", "Tareq"]
];
$transformer = new TfIdfTransformer($samples);
dd($transformer);
And when I try using transform method from the example provide in their docs
$samples = [
[1, 2, 4],
[0, 2, 1]
];
$transformer = new TfIdfTransformer($samples);
$transformer->fit($samples);
dd($transformer->transform($samples));
it gives me NULL.
Tfdftransformer.php :
<?php
declare(strict_types=1);
namespace Phpml\FeatureExtraction;
use Phpml\Transformer;
class TfIdfTransformer implements Transformer
{
/**
* #var array
*/
private $idf = [];
public function __construct(array $samples = [])
{
if (count($samples) > 0) {
$this->fit($samples);
}
}
public function fit(array $samples, ?array $targets = null): void
{
$this->countTokensFrequency($samples);
$count = count($samples);
foreach ($this->idf as &$value) {
$value = log((float) ($count / $value), 10.0);
}
}
public function transform(array &$samples): void
{
foreach ($samples as &$sample) {
foreach ($sample as $index => &$feature) {
$feature *= $this->idf[$index];
}
}
}
private function countTokensFrequency(array $samples): void
{
$this->idf = array_fill_keys(array_keys($samples[0]), 0);
foreach ($samples as $sample) {
foreach ($sample as $index => $count) {
if ($count > 0) {
++$this->idf[$index];
}
}
}
}
}

how to recursively convert from multidimensional form array to the related entity objects

I'm trying to convert a multidimensional HTML form array to its related entity (database) object classes with one to many relations and nested one to many relations.
Consider the following input exmaple (human readable):
order[id]: 1
order[note]: test note
order[ordertime]: 13. Dez. 2018 09:01
order[position][0][id]: 1
order[position][0][ordernumber]: ADSF-11
order[position][0][price]: 45.99
order[position][0][supplier][id]: 1
order[position][0][supplier][name]: test supplier 1
order[position][1][id]: 2
order[position][1][ordernumber]: ADSF-12
order[position][1][price]: 50.99
order[position][1][supplier][id]: 2
order[position][1][supplier][name]: test supplier 2
order[customer][firstname]: Human
order[customer][surname]: Being
order[customer][billingAddress][id]: 1
order[customer][billingAddress][firstname]: Human 2
order[customer][billingAddress][surname]: Being 2
order[customer][billingAddress][street]: test street 1
order[customer][billingAddress][zip]: 99999
order[customer][billingAddress][city]: test city
order[customer][shippingAddress][id]: 2
order[customer][shippingAddress][firstname]: Human 3
order[customer][shippingAddress][surname]: Being 3
order[customer][shippingAddress][street]: test street 100
order[customer][shippingAddress][zip]: 88888
order[customer][shippingAddress][city]: test city 2
We got an abstract class with empty body called AbstractEntity which every entity extends and the entities have public member variables for simple types. For arrays its access is private there are setter methods as well as addXX methods to add one entry at the end of the array (that's why reflection is needed and why we have $method1 and $method2). Additionally it parses date and time from inernationalized string to DateTime.
I would like to access them as in ORM frameworks style like Doctrine like so:
$order->getPosition()[0]->getBillingAddress()->firstname
Here is my worker class that does the main stuff:
<?php
namespace MyApp\Ajax;
use MyApp\Entity\AbstractEntity;
use MyApp\Entity\Repository;
class AjaxRequest
{
private $inputType;
private $data;
private $formatter;
private $objMapping;
private $repo;
public function __construct()
{
$this->inputType = strtolower($_SERVER['REQUEST_METHOD']) === 'post' ? \INPUT_POST : \INPUT_GET;
$this->formatter = new \IntlDateFormatter(
'de_DE',
\IntlDateFormatter::LONG,
\IntlDateFormatter::SHORT,
null,
\IntlDateFormatter::GREGORIAN,
'd. MMM Y HH:mm'
);
$this->objMapping = array(
'order' => "MyApp\\Entity\\Order",
'position' => "MyApp\\Entity\\Article",
'supplier' => "MyApp\\Entity\\Supplier",
'customer' => "MyApp\\Entity\\User",
'billingAddress' => "MyApp\\Entity\\UserAddress",
'shippingAddress' => "MyApp\\Entity\\UserAddress"
);
$this->repo = new Repository();
}
public function save()
{
$obj = $this->convertRequestToObj('order');
$this->data['success'] = $this->repo->save($obj);
$this->data['data'] = $obj;
$this->jsonResponse();
}
private function jsonResponse()
{
header('Content-type: application/json');
echo json_encode(
array(
'success' => $this->data['success'],
'data' => $this->convertToPublicObjects($this->data['data'])
)
);
}
private function convertToPublicObjects($object)
{
$names = array();
if (is_object($object) && !$object instanceof \DateTimeInterface) {
$reflection = new \ReflectionClass($object);
$columns = $reflection->getProperties();
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($columns as $column) {
$colName = $column->getName();
$method1 = 'get' . ucfirst($colName);
$method2 = 'is' . ucfirst($colName);
try {
if ($column->isPublic()) {
$names[$colName] = $column->getValue($object);
} else {
if ($reflection->hasMethod($method1) && $this->checkPublicMethods($methods, $method1)) {
$names[$colName] = $object->{$method1}();
} else {
if ($reflection->hasMethod($method2) && $this->checkPublicMethods($methods, $method2)) {
$names[$colName] = $object->{$method2}();
}
}
}
} catch (\ReflectionException $ex) {
$names[$colName] = null;
} catch (\TypeError $exc) {
$names[$colName] = null;
}
if (array_key_exists($colName, $names) && is_object($names[$colName])) {
if ($names[$colName] instanceof \DateTimeInterface) {
$names[$colName] = $this->formatter->format($names[$colName]);
} else {
$names[$colName] = $this->convertToPublicObjects($names[$colName]);
}
} elseif (array_key_exists($colName, $names) && is_array($names[$colName])) {
array_walk_recursive($names[$colName], array($this, 'walkReturnArray'));
}
}
}
return $names;
}
private function walkReturnArray(&$item, $key)
{
if (is_object($item)) {
$item = $this->convertToPublicObjects($item);
}
}
/**
* #param \ReflectionMethod[] $methods
* #param string $method
*
* #return bool
*/
private function checkPublicMethods(array $methods, string $method)
{
$found = false;
foreach ($methods as $meth) {
if ($meth->getName() === $method) {
$found = true;
break;
}
}
return $found;
}
/**
* Converts ORM like objects from the request from arrays to objects.
*
* #param string $key
*
* #return AbstractEntity
*/
private function convertRequestToObj(string $key)
{
$ar = filter_input($this->inputType, $key, \FILTER_DEFAULT, \FILTER_REQUIRE_ARRAY);
$baseObj = new $this->objMapping[$key]();
$this->mapArrayToObj($ar, $baseObj);
return $baseObj;
}
private function mapArrayToObj(array $ar, AbstractEntity $baseObj)
{
foreach ($ar as $column => $value) {
$reflection = new \ReflectionClass($baseObj);
$method1 = 'add' . ucfirst($column);
$method2 = 'set' . ucfirst($column);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if (is_array($value)) {
$newObj = new $this->objMapping[$column]();
$this->addObjectTo($methods, $method1, $method2, $baseObj, $newObj);
$reflection = new \ReflectionClass($newObj);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($value as $subCol => $subVal) {
$method2 = 'set' . ucfirst($subCol);
if (is_array($subVal)) {
if (is_numeric($subCol)) {
$this->mapArrayToObj($subVal, $newObj);
}
} else {
$this->parseSimpleType($newObj, $column, $value, $methods, $method2);
}
}
} else {
$this->parseSimpleType($baseObj, $column, $value, $methods, $method2);
}
}
}
private function parseSimpleType(AbstractEntity $obj, $column, $value, array $methods, $method2)
{
$timestamp = $this->formatter->parse($value);
if ($timestamp) {
try {
$value = new \DateTime($timestamp);
} catch (\Exception $ex) {
// nothing to do...
}
}
if ($this->checkPublicMethods($methods, $method2)) {
$obj->$method2($value);
} else {
$obj->{$column} = $value;
}
}
private function addObjectTo(array $methods, $method1, $method2, AbstractEntity $baseObj, AbstractEntity $newObj)
{
if ($this->checkPublicMethods($methods, $method1)) {
$baseObj->$method1($newObj);
} elseif ($this->checkPublicMethods($methods, $method2)) {
$baseObj->$method2($newObj);
} else {
$baseObj->{$column} = $newObj;
}
}
private function getNestedObject(AbstractEntity $obj, array $keys, $levelUp = 0)
{
if ($levelUp > 0) {
for ($i = 0; $i < $levelUp; $i++) {
unset($keys[count($keys) - 1]);
}
}
$innerObj = $obj;
$lastObj = $obj;
if (count($keys) > 0) {
foreach ($keys as $key) {
if (is_numeric($key)) {
$innerObj = $innerObj[$key];
} else {
$method = 'get' . ucfirst($key);
$reflection = new \ReflectionClass($innerObj);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
$lastObj = $innerObj;
if ($this->checkPublicMethods($methods, $method)) {
$innerObj = $innerObj->$method();
} else {
$innerObj = $innerObj->{$key};
}
}
}
if ($innerObj === null) {
$innerObj = $lastObj;
}
}
return $innerObj;
}
private function setNestedObject(array $parsedObjs, array $keys, AbstractEntity $objToAdd)
{
$ref = &$parsedObjs;
foreach ($keys as $key) {
$ref = &$ref[$key];
}
$ref = $objToAdd;
return $parsedObjs;
}
}
Let`s say this example calls the pubic method save. For some reason, it does the nesting wrong. Although the other way around, from objects to array using convertToPublicObjects works fine.
Here are my other tries:
With bypassed reference depth:
/**
* Converts ORM like objects from the request from arrays to objects.
*
* #param string $key
*
* #return AbstractEntity
*/
private function convertRequestToObj(string $key)
{
$ar = filter_input($this->inputType, $key, \FILTER_DEFAULT, \FILTER_REQUIRE_ARRAY);
$baseObj = new $this->objMapping[$key]();
$this->mapArrayToObj($ar, $baseObj, $baseObj);
return $baseObj;
}
private function mapArrayToObj(array $ar, AbstractEntity $baseObj, AbstractEntity $veryBaseObj, $refDepth = '')
{
foreach ($ar as $column => $value) {
$reflection = new \ReflectionClass($baseObj);
$method1 = 'add' . ucfirst($column);
$method2 = 'set' . ucfirst($column);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if (is_array($value) && !is_numeric($column)) {
$refDepth .= $column .',';
$newObj = new $this->objMapping[$column]();
$this->addObjectTo($methods, $method1, $method2, $baseObj, $newObj);
$this->mapArrayToObj($value, $newObj, $veryBaseObj, $refDepth);
} elseif (is_array($value) && is_numeric($column)) {
$refDepth .= $column .',';
$refKeys = explode(',', substr($refDepth, 0, strrpos($refDepth, ',')));
$toAddObj = $this->getNestedObject($veryBaseObj, $refKeys);
$column = substr($refDepth, 0, strrpos($refDepth, ','));
$column = substr($column, 0, strrpos($column, ','));
$newObj = new $this->objMapping[$column]();
$this->addObjectTo($methods, $method1, $method2, $baseObj, $newObj);
$reflection = new \ReflectionClass($newObj);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($value as $subCol => $subVal) {
if (is_array($subVal)) {
// sanitize strings like userMain,0,1,:
$refDepth = substr($refDepth, 0, strrpos($refDepth, ','));
$refDepth = substr($refDepth, 0, strrpos($refDepth, ',') + 1);
$refDepth .= $subCol . ',';
$this->mapArrayToObj($subVal, $newObj, $veryBaseObj, $refDepth);
} else {
$method2 = 'set' . ucfirst($subCol);
$this->parseSimpleType($newObj, $subCol, $subVal, $methods, $method2);
}
}
// sanitize strings like position,0,1,:
$refDepth = substr($refDepth, 0, strrpos($refDepth, ','));
$refDepth = substr($refDepth, 0, strrpos($refDepth, ',') + 1);
} else {
$refDepth = '';
$this->parseSimpleType($baseObj, $column, $value, $methods, $method2);
}
}
}
With if branches inside:
/**
* Converts ORM like objects from the request from arrays to objects.
*
* #param string $key
*
* #return AbstractEntity
*/
private function convertRequestToObj(string $key)
{
$ar = filter_input($this->inputType, $key, \FILTER_DEFAULT, \FILTER_REQUIRE_ARRAY);
$baseObj = new $this->objMapping[$key]();
$this->mapArrayToObj($ar, $baseObj, $baseObj);
return $baseObj;
}
private function mapArrayToObj(array $ar, AbstractEntity $baseObj, AbstractEntity $veryBaseObj, $refDepth = '')
{
foreach ($ar as $column => $value) {
$reflection = new \ReflectionClass($baseObj);
$method1 = 'add' . ucfirst($column);
$method2 = 'set' . ucfirst($column);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if (is_array($value)) {
$refDepth .= $column .',';
$refDepthBackup = $refDepth;
$refKeys = explode(',', substr($refDepth, 0, strrpos($refDepth, ',')));
if (is_numeric($column)) {
$column = substr($refDepth, 0, strrpos($refDepth, ','));
$column = substr($column, 0, strrpos($column, ','));
$method1 = 'add' . ucfirst($column);
$toAddObj = $this->getNestedObject($veryBaseObj, $refKeys, 2);
// sanitize strings like position,0,1,:
$refDepth = substr($refDepth, 0, strrpos($refDepth, ','));
$refDepth = substr($refDepth, 0, strrpos($refDepth, ',') + 1);
} else {
$toAddObj = $baseObj;
}
$reflection = new \ReflectionClass($toAddObj);
$method1 = 'add' . ucfirst($column);
$method2 = 'set' . ucfirst($column);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
$newObj = new $this->objMapping[$column]();
$this->addObjectTo($methods, $method1, $method2, $toAddObj, $newObj);
$this->mapArrayToObj($value, $newObj, $veryBaseObj, $refDepthBackup);
} else {
$refDepth = '';
$this->parseSimpleType($baseObj, $column, $value, $methods, $method2);
}
}
}
With an inner foreach loop:
/**
* Converts ORM like objects from the request from arrays to objects.
*
* #param string $key
*
* #return AbstractEntity
*/
private function convertRequestToObj(string $key)
{
$ar = filter_input($this->inputType, $key, \FILTER_DEFAULT, \FILTER_REQUIRE_ARRAY);
$baseObj = new $this->objMapping[$key]();
$this->mapArrayToObj($ar, $baseObj);
return $baseObj;
}
private function mapArrayToObj(array $ar, AbstractEntity $baseObj)
{
foreach ($ar as $column => $value) {
$reflection = new \ReflectionClass($baseObj);
$method1 = 'add' . ucfirst($column);
$method2 = 'set' . ucfirst($column);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if (is_array($value)) {
$newObj = new $this->objMapping[$column]();
$this->addObjectTo($methods, $method1, $method2, $baseObj, $newObj);
$reflection = new \ReflectionClass($newObj);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($value as $subCol => $subVal) {
$method2 = 'set' . ucfirst($subCol);
if (is_array($subVal)) {
if (is_numeric($subCol)) {
$this->mapArrayToObj($subVal, $newObj);
}
} else {
$this->parseSimpleType($newObj, $column, $value, $methods, $method2);
}
}
} else {
$this->parseSimpleType($baseObj, $column, $value, $methods, $method2);
}
}
}
I have not worked with orm, so not sure if this is what you wanted.
Some hints:
I used php 5.6.30 so your milage may vary.
OOP is information hiding, that means teach each class what to do, no reflection.
Use fields to implement data driven framework
Implement magic get and call to dynamically access data and objects
Each class must validate its data, not implemented here
Each class must throw and catch its own exceptions, not implemented here
Use a factory pattern to create the data classes.
The interface defines the order class facade pattern.
The trait implements the default methods for all order classes.
I toyed with the idea of using XML classes, but this seems to work okay.
This is the class file that implements an order factory pattern. When creating model objects, use the factory class (static, do not instantiate) and don't instantiate the classes directly. The getValue() handles the factory::create when required. The result is the classes create themselves using the factory.
<?php /* ormorder.php */
// Object Relational Mapping (OrmOrder)
// order OrmOrder class interface methods
interface IORM
{
// function initFields(); // this should not be public?
function toArray();
function __get($name);
function __call($name,$value);
}
// order OrmOrder class trait methods
trait FORM
{
protected $fields;
protected $data;
function __construct($data)
{
parent::__construct();
$this->initFields();
$this->setData($data);
}
// always override, never call
protected function initFields(){ $this->fields = null;}
// sometimes override, never call
protected function setData($data)
{
foreach($this->fields as $field)
if(isset($data[$field]))
$this->data[$field] = $this->getValue($field,$data[$field]);
}
// seldom override, never call
protected function getValue($field,$data) { return $data; }
function toArray(){ return $this->data; }
final function __get($name)
{
if('data' == $name)
return $this->data;
return $this->data[$name];
}
function __call($name,$value)
{
$attr = $value[0];
$val = $value[1];
$result = null;
if(in_array($name, $this->fields))
if(isset($this->data[$name]))
if(is_array($this->data[$name]))
foreach($this->data[$name] as $obj)
if($obj->$attr == $val)
{
$result = $obj;
break;
}
else $result = $this->data[$name];
return $result;
}
}
// pacify php parent::_construct()
abstract
class ORMAbstract
{
function __construct() {}
}
// Main Order class that does (almost) everything
abstract
class Orm extends ORMAbstract implements IORM
{ use FORM;
}
// you should override getValue()
class Order extends Orm
{
}
class Position extends Orm
{
}
class Supplier extends Orm
{
}
class Customer extends Orm
{
}
class Address extends Orm
{
}
// static class to return OrmOrder objects
// keep factory in sync with classes
// Call directly never implement
class OrderFactory
{
static
function create($name, $data)
{
switch($name)
{
case 'supplier': return new Item($data);
case 'position': return new LineItem($data);
case 'address': return new Address($data);
case 'customer': return new Customer($data);
case 'order': return new Order($data);
default: return null;
}
}
}
?>
The model file (and main function). Run this from command prompt
/* assume php is properly setup */
> ordermodel
This file contains the top level model, the order model used to inspect the data. The toArray() returns a multidimensional array. The OrderModel class must be instantiated and passed the (html) multidimension array.
<?php /* ordermodel.php */
require_once('ormorder.php');
// sample database, development only, delete in production
$data['order'][0]['id'] = 0;
$data['order'][0]['note'] = 'test orders';
$data['order'][0]['date'] = '23 Mar 13';
$data['order'][0]['customer'][0]['id'] = 1;
$data['order'][0]['customer'][0]['account'] = '3000293826';
$data['order'][0]['customer'][0]['name'] = 'John Doe';
$data['order'][0]['customer'][0]['billing'][0] = 'Sand Castle';
$data['order'][0]['customer'][0]['billing'][1] = '1 beach street';
$data['order'][0]['customer'][0]['billing'][2] = 'strand';
$data['order'][0]['customer'][0]['billing'][3] = 'Lagoon';
$data['order'][0]['customer'][0]['billing'][4] = 'Fairy Island';
$data['order'][0]['customer'][0]['billing'][5] = '55511';
$data['order'][0]['customer'][0]['delivery'][0] = 'Nine Acres';
$data['order'][0]['customer'][0]['delivery'][1] = '3 corn field';
$data['order'][0]['customer'][0]['delivery'][2] = 'Butterworth';
$data['order'][0]['customer'][0]['delivery'][3] = 'Foam Vale';
$data['order'][0]['customer'][0]['delivery'][4] = 'Buttress Lake';
$data['order'][0]['customer'][0]['delivery'][5] = '224433';
$data['order'][0]['customer'][0]['items'][0]['supplier'] = '4000392292';
$data['order'][0]['customer'][0]['items'][0]['stock'] = '2000225571';
$data['order'][0]['customer'][0]['items'][0]['quantity'] = 5;
$data['order'][0]['customer'][0]['items'][0]['unitprice'] = 35.3;
$data['order'][0]['customer'][0]['items'][1]['supplier'] = '4000183563';
$data['order'][0]['customer'][0]['items'][1]['stock'] = '2000442279';
$data['order'][0]['customer'][0]['items'][1]['quantity'] = 12;
$data['order'][0]['customer'][0]['items'][1]['unitprice'] = 7.4;
// Top level Order management class
// could also be an OrmOrder class
class OrderModel
{
private $orders;
function __construct($data)
{
foreach($data['order'] as $order)
$this->orders[] = OrderFactory::create('order',$order);
}
function __call($name,$value)
{
$o = null;
$attribute = $value[0];
$val = $value[1];
foreach($this->orders as $order)
{
if($order->$attribute == $val)
{
$o = $order;
break;
}
}
return $o;
}
function toArray()
{
$data = null;
foreach($this->orders as $order)
$data['order'][] = $order->toArray();
return $data;
}
}
/* development only, delete in production */
function main($data)
{
$model = new OrderModel($data);
echo $model->order('id',12)->note;
var_dump($model->order('date',
'23 Mar 13')->customer('account','3000293826')->delivery->data);
// var_dump($model->toArray());
}
main($data);
?>
The output should be similar to:
PHP Notice: Trying to get property 'note' of non-object in C:\Users\Peter\Docum
ents\php\ordermodel.php on line 70
Notice: Trying to get property 'note' of non-object in C:\Users\Peter\Documents\
php\ordermodel.php on line 70
array(6) {
[0]=>
string(10) "Nine Acres"
[1]=>
string(12) "3 corn field"
[2]=>
string(11) "Butterworth"
[3]=>
string(9) "Foam Vale"
[4]=>
string(13) "Buttress Lake"
[5]=>
string(6) "224433"
}
Hopefully this does the kind of inspection you're looking for, probably not the same as Doctrine, but perhaps close enough to be useful.
UPDATE *
To implement the answer in your code try this:
<?PHP
require_once('ordermodel.php');
/*..... */
private function jsonResponse()
{
header('Content-type: application/json');
echo json_encode(
array(
'success' => $this->data['success'],
'data' => new OrderModel($this->data['data'])
)
);
}
?>

Zend- 1048 Column 'member_login' cannot be null

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.

add clause WHERE for admin generator

I generated with Admin generator this:
abstract class autoNewsActions extends sfActions
{
public function preExecute()
{
$this->configuration = new newsGeneratorConfiguration();
if (!$this->getUser()->hasCredential($this->configuration->getCredentials($this->getActionName())))
{
$this->forward(sfConfig::get('sf_secure_module'), sfConfig::get('sf_secure_action'));
}
$this->dispatcher->notify(new sfEvent($this, 'admin.pre_execute', array('configuration' => $this->configuration)));
$this->helper = new newsGeneratorHelper();
parent::preExecute();
}
public function executeIndex(sfWebRequest $request)
{
// sorting
if ($request->getParameter('sort') && $this->isValidSortColumn($request->getParameter('sort')))
{
$this->setSort(array($request->getParameter('sort'), $request->getParameter('sort_type')));
}
// pager
if ($request->getParameter('page'))
{
$this->setPage($request->getParameter('page'));
}
$this->pager = $this->getPager();
$this->sort = $this->getSort();
}
public function executeFilter(sfWebRequest $request)
{
$this->setPage(1);
if ($request->hasParameter('_reset'))
{
$this->setFilters($this->configuration->getFilterDefaults());
$this->redirect('#news');
}
$this->filters = $this->configuration->getFilterForm($this->getFilters());
$this->filters->bind($request->getParameter($this->filters->getName()));
if ($this->filters->isValid())
{
$this->setFilters($this->filters->getValues());
$this->redirect('#news');
}
$this->pager = $this->getPager();
$this->sort = $this->getSort();
$this->setTemplate('index');
}
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm();
$this->news = $this->form->getObject();
}
public function executeCreate(sfWebRequest $request)
{
$this->form = $this->configuration->getForm();
$this->news = $this->form->getObject();
$this->processForm($request, $this->form);
$this->setTemplate('new');
}
public function executeEdit(sfWebRequest $request)
{
$this->news = $this->getRoute()->getObject();
$this->form = $this->configuration->getForm($this->news);
}
public function executeUpdate(sfWebRequest $request)
{
$this->news = $this->getRoute()->getObject();
$this->form = $this->configuration->getForm($this->news);
$this->processForm($request, $this->form);
$this->setTemplate('edit');
}
public function executeDelete(sfWebRequest $request)
{
$request->checkCSRFProtection();
$this->dispatcher->notify(new sfEvent($this, 'admin.delete_object', array('object' => $this->getRoute()->getObject())));
if ($this->getRoute()->getObject()->delete())
{
$this->getUser()->setFlash('notice', 'The item was deleted successfully.');
}
$this->redirect('#news');
}
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
if ($form->isValid())
{
$notice = $form->getObject()->isNew() ? 'The item was created successfully.' : 'The item was updated successfully.';
try {
$news = $form->save();
} catch (Doctrine_Validator_Exception $e) {
$errorStack = $form->getObject()->getErrorStack();
$message = get_class($form->getObject()) . ' has ' . count($errorStack) . " field" . (count($errorStack) > 1 ? 's' : null) . " with validation errors: ";
foreach ($errorStack as $field => $errors) {
$message .= "$field (" . implode(", ", $errors) . "), ";
}
$message = trim($message, ', ');
$this->getUser()->setFlash('error', $message);
return sfView::SUCCESS;
}
$this->dispatcher->notify(new sfEvent($this, 'admin.save_object', array('object' => $news)));
if ($request->hasParameter('_save_and_add'))
{
$this->getUser()->setFlash('notice', $notice.' You can add another one below.');
$this->redirect('#news_new');
}
else
{
$this->getUser()->setFlash('notice', $notice);
$this->redirect(array('sf_route' => 'news_edit', 'sf_subject' => $news));
}
}
else
{
$this->getUser()->setFlash('error', 'The item has not been saved due to some errors.', false);
}
}
protected function getFilters()
{
return $this->getUser()->getAttribute('news.filters', $this->configuration->getFilterDefaults(), 'admin_module');
}
protected function setFilters(array $filters)
{
return $this->getUser()->setAttribute('news.filters', $filters, 'admin_module');
}
protected function getPager()
{
$pager = $this->configuration->getPager('News');
$pager->setQuery($this->buildQuery());
$pager->setPage($this->getPage());
$pager->init();
return $pager;
}
protected function setPage($page)
{
$this->getUser()->setAttribute('news.page', $page, 'admin_module');
}
protected function getPage()
{
return $this->getUser()->getAttribute('news.page', 1, 'admin_module');
}
protected function buildQuery()
{
$tableMethod = $this->configuration->getTableMethod();
if (null === $this->filters)
{
$this->filters = $this->configuration->getFilterForm($this->getFilters());
}
$this->filters->setTableMethod($tableMethod);
$query = $this->filters->buildQuery($this->getFilters());
$this->addSortQuery($query);
$event = $this->dispatcher->filter(new sfEvent($this, 'admin.build_query'), $query);
$query = $event->getReturnValue();
return $query;
}
protected function addSortQuery($query)
{
if (array(null, null) == ($sort = $this->getSort()))
{
return;
}
if (!in_array(strtolower($sort[1]), array('asc', 'desc')))
{
$sort[1] = 'asc';
}
$query->addOrderBy($sort[0] . ' ' . $sort[1]);
}
protected function getSort()
{
if (null !== $sort = $this->getUser()->getAttribute('news.sort', null, 'admin_module'))
{
return $sort;
}
$this->setSort($this->configuration->getDefaultSort());
return $this->getUser()->getAttribute('news.sort', null, 'admin_module');
}
protected function setSort(array $sort)
{
if (null !== $sort[0] && null === $sort[1])
{
$sort[1] = 'asc';
}
$this->getUser()->setAttribute('news.sort', $sort, 'admin_module');
}
protected function isValidSortColumn($column)
{
return Doctrine_Core::getTable('News')->hasColumn($column);
}
}
how can i make add for this clause WHERE id > 10 for list in index? maybe in generator.yml?
Use the table_method generator.yml option.
In generator.yml:
config:
list:
table_method: buildQueryForAdminIndex
In NewsTable:
public function buildQueryForAdminIndex(Doctrine_Query $q)
{
$q->addWhere($q->getRootAlias() . '.id > ?', 10);
return $q;
}
I should note that hardcoding that id is poor form. It should be abstracted so that it is clear why the value is 10.

Multiple Tables in Zend based on the Quick Start Guide Example

I am new to Zend and have been attempting to follow the Zend Quick Start Guide's example of using Data Mappers and extending Zend_Db_Table_Abstract. I think I've grasped the general concepts, but I am now wondering, how I would go about modifying the guide's example code to allow for multiple tables.
Here is the part of the code I am currently interested in modifying:
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_Guestbook');
}
return $this->_dbTable;
}
I have changed it to this:
protected $_dbTables;
public function setDbTable($dbTable, $tableName)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTables[$tableName] = $dbTable;
return $this;
}
public function getDbTables()
{
if (null === $this->_dbTables) {
$this->setDbTable('Application_Model_DbTable_Courses', 'courses');
$this->setDbTable('Application_Model_DbTable_CourseTimes', 'course_times');
}
return $this->_dbTables;
}
Is this a correct way to go about implementing multiple tables within the Data Mapper pattern or would you do it differently? Thanks for your help in advance!
Assuming that you want to return data from related tables, you should either add queries with joins to one Zend_Db_Table or use Zend_Db_Table_Relationships to fetch associated data. The benefit of using Joins is that you will only do one query over many when using Relationships. The drawback is that joined tables wont return Zend_Db_Table_Row objects (iirc), but since you are going to map them onto your Domain objects anyway, it's not that much of an issue.
Structurally, you can do like I suggested in How to change Zend_Db_Table name within a Model to insert in multiple tables. Whether you create a Gateway of Gateways or simply aggregate the Table Gateways in the DataMapper directly is really up to you. Just compose them as you see fit.
I'm not sure if it's the best practice, but, this is my abstract mapper:
<?php
abstract class Zf_Model_DbTable_Mapper
{
protected $_db;
protected $_dbTable = null;
protected $_systemLogger = null;
protected $_userLogger = null;
public function __construct()
{
$this->_systemLogger = Zend_Registry::get('systemLogger');
$this->_userLogger = Zend_Registry::get('userLogger');
// Set the adapter
if(null !== $this->_dbTable)
{
$tableName = $this->_dbTable;
$this->_db = $this->$tableName->getAdapter();
}
}
public function __get($value)
{
if(isset($this->$value))
{
return $this->$value;
}
$dbTable = 'Model_DbTable_' . $value;
$mapper = 'Model_' . $value;
if(class_exists($dbTable))
{
return new $dbTable;
}
elseif(class_exists($mapper))
{
return new $mapper;
}
else
{
throw new Exception("The property, DbTable or Mapper \"$value\" doesn't exists");
}
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function getById($id)
{
$resource = $this->getDefaultResource();
$id = (int)$id;
$row = $resource->fetchRow('id =' . $id);
if (!$row) {
throw new Exception("Count not find row $id");
}
return $row;
}
public function getAll()
{
$resource = $this->getDefaultResource();
return $resource->fetchAll()->toArray();
}
public function save(Zf_Model $Model)
{
$dbTable = $this->getDefaultResource();
$data = $Model->toArray();
if(false === $data) return false;
if(false === $Model->isNew())
{
if(1 == $dbTable->update($data, 'id =' . (int)$Model->getId()))
{
return $Model;
}
}
else
{
$id = $dbTable->insert($data);
if($id)
{
$Model->setId($id);
return $Model;
}
}
return false;
}
public function remove($id)
{
return $this->getDefaultResource()->delete('id =' . (int) $id);
}
protected function getDefaultResource()
{
if(empty($this->_dbTable))
{
throw new Exception('The $_dbTable property was not set.');
}
$classname = 'Model_DbTable_' . $this->_dbTable;
if(!class_exists($classname))
{
throw new Exception("The Model_DbTable_\"$classname\" class was not found.");
}
return new $classname;
}
protected function getDefaultModel()
{
return current($this->_models);
}
protected function getResources()
{
return $this->_resources;
}
}
And this is one for my implemented mappers:
<?php
class Model_TwitterPostsMapper extends Zf_Model_DbTable_Mapper
{
/*
* Data Source
* #var string Zend_Db_Table name
*/
protected $_dbTable = 'TwitterPosts';
public function recordExists($Item)
{
$row = $this->TwitterPosts->fetchRow($this->TwitterPosts->select()->where('status_id =?', $Item->getSource()->getStatusId()));
if($row)
{
return $row->id;
}
return false;
}
public function getLastUpdate($options)
{
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), 't.created_at')
->join(array('u' => 'twt_users'), 't.user_id = u.id', '')
->order('t.created_at DESC');
if($options['user_id'])
{
$select->where("t.user_id = ?", $options['user_id']);
}
if($options['terms'])
{
if(is_array($options['terms']))
{
$condition = '';
foreach($options['terms'] as $i => $term)
{
$condition .= ($i > 0) ? ' OR ' : '';
$condition .= $this->getAdapter()->quoteInto('content LIKE ?',"%$term%");
}
if($condition)
{
$select->where($condition);
}
}
}
return $this->TwitterPosts->fetchRow($select)->created_at;
}
public function getSinceId($term = null)
{
$select = $this->TwitterPosts->select()->setIntegrityCheck(false)
->from('twt_tweets_content', 'status_id')
->where('MATCH(content) AGAINST(? IN BOOLEAN MODE)', "$term")
->order('status_id ASC')
->limit(1);
//echo $select; exit;
$tweet = $this->TwitterPosts->fetchRow($select);
if(null !== $tweet) return $tweet->status_id;
return 0;
}
public function getAllByStatusId($statuses_id)
{
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('t.id', 't.user_id', 't.status_id','t.user_id'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', array('u.screen_name', 'u.profile_image'))
->where('status_id IN(?)', $statuses_id);
$rows = $this->TwitterPosts->fetchAll($select);
$Posts = array();
foreach($rows as $row)
{
// Here we populate the models only with the specific method return data
$data = $row->toArray();
$Post = new Model_TwitterPost($data['id']);
$Post->populate($data);
$User = new Model_TwitterUser($data['user_id']);
$User->populate($data);
$Post->setUser($User);
$Posts[] = $Post;
}
return $Posts;
}
public function getAllSince($since_id)
{
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('t.status_id','t.user_id'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', array('u.screen_name', 'u.profile_image'))
->where('status_id > ?', $since_id)
->order('t.datetime DESC');
$rows = $this->TwitterPosts->fetchAll($select);
$Posts = array();
foreach($rows as $row)
{
// Here we populate the models only with the specific method return data
// TODO: This is not a truly lazy instatiation, since there's no way to get the not setted properties
$data = $row->toArray();
$Post = new Model_TwitterPost($data);
$User = new Model_TwitterUser($data);
$Post->setUser($User);
$Posts[] = $Post;
}
return $Posts;
}
public function getTotalRatedItems($options)
{
$options = $this->prepareOptions($options);
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('COUNT(DISTINCT t.id) AS total','r.rate'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', '')
->join(array('r' => 'twt_tweets_rate'), 't.id = r.tweet_id', array('r.rate'))
->group('r.rate')
->order('t.datetime DESC');
$select = $this->prepareSelect($select, $options);
$rates = $this->TwitterPosts->fetchAll($select)->toArray();
$itemsRated = array('Green' => 0, 'Yellow' => 0, 'Orange' => 0, 'Red' => 0, 'Gray' => 0);
foreach ($rates as $rate)
{
$itemsRated[$rate['rate']] = $rate['total'];
}
return $itemsRated;
}
public function getUsersActivity($options)
{
$options = $this->prepareOptions($options);
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('COUNT(DISTINCT t.id) AS total','DATE(t.datetime) AS datetime'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', '')
->joinLeft(array('r' => 'twt_tweets_rate'), 't.id = r.tweet_id', '')
->group('t.user_id')
->order('t.datetime DESC');
$select = $this->prepareSelect($select, $options);
$activity = $this->TwitterPosts->fetchAll($select)->toArray();
return $activity;
}
public static function prepareOptions($options)
{
if(!is_array($options))
{
$options = array();
}
date_default_timezone_set('America/Sao_Paulo');
if(Zend_Date::isDate($options['start_date']))
{
$date = new Zend_Date($options['start_date']);
$date->setTime('00:00:00');
$date->setTimezone('UTC');
$options['start_date'] = $date->toString('yyyy-MM-dd HH:mm:ss');
}
if(Zend_Date::isDate($options['end_date']))
{
$date = new Zend_Date($options['end_date']);
$date->setTime('23:59:59');
$date->setTimezone('UTC');
$options['end_date'] = $date->toString('yyyy-MM-dd HH:mm:ss');
}
date_default_timezone_set('UTC');
$options['mainTerms'] = array();
if(!empty($options['terms']) && !is_array($options['terms']))
{
$options['mainTerms'] = explode(' ', $options['terms']);
}
if(!is_array($options['terms']))
{
$options['terms'] = array();
}
if($options['group_id'] || $options['client_id'])
{
$TwitterSearches = new Model_DbTable_TwitterSearches();
$options['terms'] = array_merge($TwitterSearches->getList($options),$options['terms']);
if(empty($options['terms']))
{
$options['terms'] = array();
}
}
return $options;
}
public static function prepareSelect($select, $options)
{
if($options['start_date'])
{
$select->where('t.datetime >= ?', $options['start_date']);
}
if($options['end_date'])
{
$select->where('t.datetime <= ?', $options['end_date']);
}
foreach($options['mainTerms'] as $mainTerm)
{
$select->where('t.content LIKE ?', "%$mainTerm%");
}
if($options['user_id'])
{
$select->where("t.user_id = ?", $options['user_id']);
}
if($options['terms'])
{
$select->where('MATCH (t.content) AGASINT(?)', $options['terms']);
}
if($options['rate'])
{
if($options['rate'] == 'NotRated')
{
$select->where('r.rate IS NULL');
}
else
{
$select->where('r.rate = ?', $options['rate']);
}
}
if($options['last_update'])
{
$select->where('t.created_at > ?', $options['last_update']);
}
if($options['max_datetime'])
{
$select->where('t.created_at < ?', $options['max_datetime']);
}
return $select;
}
}
The Model:
<?php
class Model_TwitterPost extends Zf_Model
{
private $_name = 'twitter';
protected $_properties = array(
'id',
'status_id',
'user_id',
'content'
);
protected $_User = null;
public function setUser(Zf_Model $User)
{
$this->_User = $User;
}
public function getUser()
{
return $this->_User;
}
public function getPermalink()
{
return 'http://twitter.com/' . $this->screen_name . '/' . $this->status_id;
}
public function hasTerm($term)
{
if(preg_match("/\b$term\b/i", $this->getContent()))
{
return true;
}
return false;
}
public function getEntityName()
{
return $this->_name;
}
public function getUserProfileLink()
{
return $this->getUser()->getProfileLink() . '/status/' . $this->getStatusId();
}
}
Abstract model (Generic Object):
<?php
abstract class Zf_Model
{
protected $_properties = array();
protected $_modified = array();
protected $_data = array();
protected $_new = true;
protected $_loaded = false;
public function __construct($id=false)
{
$id = (int)$id;
if(!empty($id))
{
$this->_data['id'] = (int)$id;
$this->setNew(false);
}
}
public function populate($data)
{
if(is_array($data) && count($data))
{
foreach($data as $k => $v)
{
if(in_array($k,$this->_properties))
{
$this->_data[$k] = $v;
}
}
}
$this->setLoaded(true);
}
public function setNew($new=true)
{
$this->_new = (bool)$new;
}
public function isNew()
{
return $this->_new;
}
public function setLoaded($loaded = true)
{
$this->_loaded = (bool)$loaded;
}
public function isLoaded()
{
return $this->_loaded;
}
public function __call($methodName, $args) {
if(method_exists($this, $methodName))
{
return $this->$methodName($args);
}
$property = $methodName;
if (preg_match('~^(set|get)(.*)$~', $methodName, $matches))
{
$filter = new Zend_Filter_Word_CamelCaseToUnderscore();
$property = strtolower($filter->filter($matches[2]));
if(in_array($property, $this->_properties))
{
if('set' == $matches[1])
{
$this->_data[$property] = $args[0];
if(true === $this->isLoaded())
{
$this->_modified[$property] = true;
}
return $this;
}
elseif('get' == $matches[1])
{
if(array_key_exists($property, $this->_data))
{
return $this->_data[$property];
}
throw new Exception("The property $property or $methodName() method was not setted for " . get_class($this));
}
}
}
throw new Exception("The property '$property' doesn't exists.");
}
public function __get($key)
{
if(isset($this->_data[$key]))
{
return $this->_data[$key];
}
return $this->$key;
}
public function __set($key,$value)
{
if(array_key_exists($key,$this->_properties))
{
$this->_data[$key] = $value;
return;
}
$this->$key = $value;
}
public function getId()
{
return (!$this->_data['id']) ? null : $this->_data['id'];
}
public function toArray()
{
// If it's a new object
if(true === $this->isNew())
{
return $this->_data;
}
// Else, if it's existing object
$data = array();
foreach($this->_modified as $k=>$v)
{
if($v)
{
$data[$k] = $this->_data[$k];
}
}
if(count($data))
{
return $data;
}
return false;
}
public function reload()
{
$this->_modified = array();
}
}

Categories