Initialize a hydrator result set and aggregate in ZF2 - php

I am joining two tables and have successfully managed to write a hydrator that outputs the following array:
$sql = new Sql($this->dbAdapter);
$select = $sql->select('misc_damage');
$select->where(array('vehicle_id = ?' => $id))->order('date_added DESC');
$select->join('user','user.user_id = misc_damage.added_user_id',
array(
'user_display_name' => 'display_name',
'user_email' => 'email',
'user_username' => 'username'
),
'left');
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
if ($result instanceof ResultInterface && $result->isQueryResult()) {
$hydrator = new AggregateHydrator();
$hydrator->add(new ClassMethods());
$hydrator->add(new \Application\Hydrator\UserHydrator());
$miscDamage = $hydrator->hydrate($result->current(), new \Application\Model\Miscdamage());
var_dump($miscDamage);
die();
}
This produces 1 result:
/var/www/zf-skeleton/module/Application/src/Application/Mapper/ZendDbSqlMapper.php:95:
object(Application\Model\Miscdamage)[655]
protected 'id' => string '97' (length=2)
protected 'vehicle_id' => string '3' (length=1)
protected 'added_user_id' => string '1' (length=1)
protected 'description' => string 'sdfsdsdf' (length=8)
protected 'date_added' => string '2016-04-15 08:19:17' (length=19)
protected 'date_repaired' => null
protected 'repaired_user_id' => null
protected 'status' => string '0' (length=1)
protected 'user' =>
object(Application\Model\User)[664]
protected 'user_id' => string '1' (length=1)
protected 'username' => null
protected 'email' => string 'alex#home.com' (length=13)
protected 'display_name' => string 'Alex' (length=4)
There should be multiple results as each vehicle can have multiple damage entries. How would I go about using HydratingResultSet with my AggregateHydrator? I would also like to initialize the result: return $resultSet->initialize($result);
Any help is appreciated!

I found out how to do this. Need to use HydratingResultSet and Reflection:
use Zend\Stdlib\Hydrator\Reflection as ReflectionHydrator;
if ($result instanceof ResultInterface && $result->isQueryResult()) {
$hydrator = new AggregateHydrator();
$hydrator->add(new ClassMethods());
$hydrator->add(new \Application\Hydrator\UserHydrator());
$resultSet = new HydratingResultSet(new ReflectionHydrator, new \Application\Model\Miscdamage());
$resultSet->setHydrator($hydrator);
return $resultSet->initialize($result);
}

Related

PHP display nested Object with array of other Object as 1 nested array

I have big Object with protected properties and a property can be an array of other Objects. My goal is to print this entire Object as a single nested array. So I need to convert the object to an array.
I've tried doing:
$result = (array) $object;
But this converts only the highest lever object to an array and it messes up my protected properties names with weird question mark signs.
I've also tried something like this but this simply returns an empty array:
$result= json_decode(json_encode($object), true);
Here is what my object looks like:
object(Handling\Model\SearchBooking\Booking)[133]
protected 'jabooknr' => string '018024709' (length=9)
protected 'jitsbooknr' => string '' (length=9)
protected 'status' => string 'Y' (length=1)
protected 'platform' => int 4
protected 'agentid' => string '' (length=6)
protected 'paymentInfo' => null
protected 'transports' =>
array (size=2)
0 =>
object(Handling\Model\SearchBooking\Transport)[145]
protected 'depdate' =>
object(DateTime)[146]
public 'date' => string '2016-12-06 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
protected 'carriercode' => string 'TB' (length=2)
protected 'carriernumber' => string '2067' (length=4)
protected 'brochure' => string '' (length=6)
protected 'pax' =>
array (size=2)
0 =>
object(Handling\Model\SearchBooking\Pax)[147]
protected 'id' => int 1
protected 'title' => string 'MRS' (length=3)
protected 'firstname' => string 'MA' (length=7)
protected 'name' => string 'BEN' (length=5)
protected 'age' => int 58
protected 'luggage' => int 20
protected 'handLuggage' => null
1 =>
object(Handling\Model\SearchBooking\Pax)[148]
protected 'id' => int 2
protected 'title' => string 'MR' (length=2)
protected 'firstname' => string 'P' (length=6)
protected 'name' => string 'FT' (length=4)
protected 'age' => int 60
protected 'luggage' => int 20
protected 'handLuggage' => null
protected 'departureAirport' => string 'BRU' (length=3)
protected 'arrivalAirport' => string 'AGP' (length=3)
1 =>
object(Handling\Model\SearchBooking\Transport)[149]
protected 'depdate' =>
object(DateTime)[150]
public 'date' => string '2016-12-13 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
protected 'carriercode' => string 'TB' (length=2)
protected 'carriernumber' => string '2068' (length=4)
protected 'brochure' => string '' (length=6)
protected 'pax' =>
array (size=2)
0 =>
object(Handling\Model\SearchBooking\Pax)[151]
protected 'id' => int 1
protected 'title' => string 'MRS' (length=3)
protected 'firstname' => string 'MANE' (length=7)
protected 'name' => string 'BN' (length=5)
protected 'age' => int 58
protected 'luggage' => int 20
protected 'handLuggage' => null
1 =>
object(Handling\Model\SearchBooking\Pax)[152]
protected 'id' => int 2
protected 'title' => string 'MR' (length=2)
protected 'firstname' => string 'PIRE' (length=6)
protected 'name' => string 'FYT' (length=4)
protected 'age' => int 60
protected 'luggage' => int 20
protected 'handLuggage' => null
protected 'departureAirport' => string 'AGP' (length=3)
protected 'arrivalAirport' => string 'BRU' (length=3)
protected 'extraLuggage' => null
EDIT
I have a method in my class where I "find" the result that looks like this:
public function findBooking()
{
//here happens a bunch of logic to get the right result
var_dump($object); exit; // this is the result that is show above
return $object;
}
There are a few issues, that make this difficult.
Property visibility, (private, protected) can cause issues when trying to read them outside of the class, proper. This is expected behavior as that's the point to not use public.
Classes are different. They are well defined and we know them ahead of time, but they are too diverse to account of all property names, at least not with a lot of wasted effort. Not to mention defining them "hard coding" would bite you later as it would make it difficult to maintain. For example if one of the packages does an update and you have coded the property names in you may have issues if they change them. On top of this given that these properties are not part of the classes Public "API" but instead part of the internals, it would not be unreasonable for them to change.
Properties can contain a mix of data types, including other classes or objects. This can make it challenging to handle.
Classes are part of other packages/frameworks and editing them is not practical, this restricts us to working outside of these classes.
So given these difficulties I would recommend using reflection to access the protected properties. Reflection allows you to inspect the definition of classes (and other stuff).
function jsonSerialize($obj){
return json_encode(toArray($obj));
}
function toArray($obj){
$R = new ReflectionObject($obj);
$proerties = $R->getProperties();
$data = [];
foreach($proerties as $k => $v){
$v->setAccessible(true);
$property = $v->getName();
$value = $v->getValue($obj);
if(!is_object($value)){
$data[$property] = $value;
}else if( is_a($obj,'\\DateTime')){
//if its a descendant of Datetime, get a formatted date.
// you can add other special case classes in this way
$data[$property] = $value->format('Y-m-d H:i:s');
}else{
$data[$property] = toArray($value); //call recursively
}
}
return $data;
}
So assume we have these classes
class foo{
private $bar; //private nested object
public function __construct(){
$this->bar = new bar();
}
}
class bar{
private $something = 'hello';
}
$obj = new foo;
echo jsonSerialize($obj);
See it in a sandbox here
Outputs:
{"bar":{"something":"hello"}}
Also of note is we have a special consideration for the DateTime class. Instead of getting all the properties of this we just want the date (probably) formatted in some standard way. So by using is_a() (I'm old school) we can tell if the Object $value has a given class as an ancestor of it. Then we just do our formatting.
There are probably a few special cases like this, so I wanted to mention how to handle them.
Though it is an old query, most answers are not easy to follow. So I tried to simplify the code for this specific question.
The cleaner way to get JSON objects is by implementing the JsonSerializable interface.
class Booking implements JsonSerializable
{
protected $jabooknr;
protected $platform;
//Other attributes ....
//Array of tronsport
protected $transports;
protected $extraLuggage;
public function jsonSerialize()
{
return [
'jabooknr'=> $this->jabooknr,
'platform'=> $this->platform,
'transports' => [json_encode($this->transports)
],
'$extraLuggage' => $this->extraLuggage
];
}
public function __construct($jabooknr, $platform){
$this->jabooknr = $jabooknr;
$this->platform = $platform;
$this->transports=[new Transport()];
}
}
class Transport implements JsonSerializable{
protected $carriercode;
protected $carriernumber;
//Array of Pax
protected $pax ;
public function jsonSerialize()
{
return [
'carriercode'=> $this->carriercode,
'carriernumber'=> $this->carriernumber
];
}
}
$booking = new Booking('018024709',25);
echo json_encode($booking);

ZF2 Hydrator and LEFT JOIN

I am hydrating a MySQL result:
public function getMiscdamage($id)
{
$sql = new Sql($this->dbAdapter);
$select = $sql->select('misc_damage');
$select->where(array('vehicle_id = ?' => $id))->order('date_added DESC');
$select->join('user','user.user_id = misc_damage.added_user_id', array('added_user_display_name' => 'display_name'), 'left');
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
if ($result instanceof ResultInterface && $result->isQueryResult()) {
$resultSet = new HydratingResultSet(new \Zend\Stdlib\Hydrator\ClassMethods(), new \Application\Model\Miscdamage());
return $resultSet->initialize($result);
}
throw new \InvalidArgumentException("Vehicle with given ID:{$id} not found.");
}
The query has a LEFT join, and the query DOES work. But when I var_dump in a loop through, the added_user_display_name` is null:
/var/www/zf-skeleton/module/Application/view/application/live/details.phtml:337:
object(Application\Model\Miscdamage)[662]
protected 'id' => string '100' (length=3)
protected 'vehicle_id' => string '8' (length=1)
protected 'added_user_id' => string '1' (length=1)
protected 'added_user_display_name' => null
protected 'description' => string 'dfgdggfdd' (length=9)
protected 'date_added' => string '2016-04-15 12:26:40' (length=19)
protected 'date_repaired' => null
protected 'repaired_user_id' => null
protected 'status' => string '0' (length=1)
How do I go about using a left join and hydrator? Any help is appreciated.
Your sql result is a response from a functionnal need of data. Therefore, you should use a custom hydrator built specifically for this functionnal need. This hydrator is often an aggregator hydrator which uses multiple hydrator to build a custom model. You should check it out

Check if row exists and if it doesn't insert data

I have an Eloquent model that is creating new entries into one of my tables. The problem I am having is each time the page runs it is inserting the data over and over. How can I check if that data already exists in that table and if it doesn't exist, then insert that particular data?
foreach($xml as $product) {
//var_dump($product); die;
Order::where('id', '=', $product->reference)->update(
array(
'invoice_id' => $product['orderid'],
'shipped' => $product['shipped'],
'processed' => $product['processed'],
'problem' => $product['problem'],
)
);
$tracking = new OrderTrack();
$tracking->invoice_id = $product['orderid'];
$tracking->shipper = $product->shipping->shipper;
$tracking->shipping_method = $product->shipping->shipping_method;
$tracking->shipping_date = $product->shipping->shipping_date;
$tracking->shipping_cost = $product->shipping->shipping_cost;
$tracking->tracking_number = $product->shipping->tracking_number;
if(!empty($product->shipping->shipping_notes)) {
$tracking->shipping_notes = $product->shipping->shipping_notes;
}
$tracking->save();
echo'done';
}
The OrderTrack is the section of code I am concerned about. It will create a new entry no matter if the row already exists or not.
$xml var_dump
object(SimpleXMLElement)[294]
public 'order' =>
array (size=15)
0 =>
object(SimpleXMLElement)[295]
public '#attributes' =>
array (size=7)
...
public 'reference' => string '3053' (length=4)
public 'reference2' =>
object(SimpleXMLElement)[310]
...
public 'reference3' =>
object(SimpleXMLElement)[311]
...
public 'invoice' =>
object(SimpleXMLElement)[312]
...
public 'order_date' => string '2014-08-14 03:00:06' (length=19)
public 'billing_date' => string '2014-08-14 00:00:00' (length=19)
public 'changed' => string '2014-08-14 13:02:15' (length=19)
public 'sender' =>
object(SimpleXMLElement)[313]
...
public 'receiver' =>
object(SimpleXMLElement)[314]
...
public 'shipping' =>
object(SimpleXMLElement)[315]
...
public 'products' =>
object(SimpleXMLElement)[316]
...
1 =>
object(SimpleXMLElement)[296]
public '#attributes' =>
array (size=7)
...
public 'reference' => string '3054' (length=4)
public 'reference2' =>
object(SimpleXMLElement)[328]
...
public 'reference3' =>
object(SimpleXMLElement)[329]
...
public 'invoice' =>
object(SimpleXMLElement)[330]
...
public 'order_date' => string '2014-08-13 22:00:02' (length=19)
public 'billing_date' => string '2014-08-14 00:00:00' (length=19)
public 'changed' => string '2014-08-14 13:02:20' (length=19)
public 'sender' =>
object(SimpleXMLElement)[331]
...
public 'receiver' =>
object(SimpleXMLElement)[332]
...
public 'shipping' =>
object(SimpleXMLElement)[333]
...
public 'products' =>
object(SimpleXMLElement)[334]
...
Took advantage of firstOrNew
$tracking = OrderTrack::firstOrNew(array('invoice_id' => $product['orderid']));
$tracking->invoice_id = $product['orderid'];
$tracking->shipper = $product->shipping->shipper;
$tracking->shipping_method = $product->shipping->shipping_method;
$tracking->shipping_date = $product->shipping->shipping_date;
$tracking->shipping_cost = $product->shipping->shipping_cost;
$tracking->tracking_number = $product->shipping->tracking_number;
if(!empty($product->shipping->shipping_notes)) {
$tracking->shipping_notes = $product->shipping->shipping_notes;
}
$tracking->save();

How to create a form in ZF2 using the fieldsets, validators, strategies and the "Element Collections"

I have spent a week to understand how to create a form in Zend Framework 2 using the fieldset and the element collection in order to use the right validator and a good field strategy.
I am trying to create an EAV (Entity, Attribute, Value) structure in my Open Source Project but I have some difficulty with the Validators and strategies.
I am creating the Product management in my software where the fields of the form are described within the attributes table
The ProductForm is:
namespace ProductAdmin\Form;
use Product\Entity\Product;
use Zend\Form\Annotation\InputFilter;
use Zend\InputFilter\Input;
use Zend\Form\Form;
use Zend\Stdlib\Hydrator\ClassMethods;
use Base\Hydrator\Strategy\DateTimeStrategy;
use Zend\Stdlib\Hydrator;
class ProductForm extends Form
{
public function init ()
{
$hydrator = new ClassMethods();
// $hydrator->addStrategy('createdat', new DateTimeStrategy()); This is just an old strategy test for the first form field level.
$this->setAttribute('method', 'post');
$this->setHydrator($hydrator)->setObject(new \Product\Entity\Product());
$this->add(array('type' => 'hidden', 'name' => 'type_id'));
$this->add(array('type' => 'hidden', 'name' => 'attribute_set_id'));
$this->add(
array('name' => 'submit',
'attributes' => array('type' => 'submit',
'class' => 'btn btn-success',
'value' => _('Save'))));
$this->add(
array('name' => 'id',
'attributes' => array('type' => 'hidden')));
}
/**
* Prepare the attribute form
*
* #param $attributes array
* #return \ProductAdmin\Form\ProductForm
*/
public function createAttributesElements (array $attributes)
{
$customHydrator = new Hydrator\ClassMethods();
$parentFilter = new \Zend\InputFilter\InputFilter();
$filterChain = new \Zend\Filter\FilterChain();
$fieldset = new \Zend\Form\Fieldset('attributes');
$fieldset->setFormFactory($this->getFormFactory()); // thanks to jurians #zftalk irc
$fieldInput = null;
$inputFilter = new \Zend\InputFilter\InputFilter();
foreach ($attributes as $attribute) {
$dateformat = "";
$name = $attribute->getName();
$label = $attribute->getLabel() ? $attribute->getLabel() : "-";
$input = $attribute->getInput() ? $attribute->getInput() : "text";
$type = $attribute->getType() ? $attribute->getType() : "string";
$isRequired = $attribute->getIsRequired();
$sourceModel = $attribute->getSourceModel();
$filters = $attribute->getFilters();
$cssStyles = $attribute->getCss();
$filterChain->attachByName('null'); // set as default
$typeSource = !empty($sourceModel) ? $sourceModel : $input;
// Handle the dates
if(!empty($type) && $type == "datetime"){
$customHydrator->addStrategy($name, new DateTimeStrategy());
$typeSource = 'Zend\Form\Element\DateTime';
$dateformat = "d/m/Y";
}
$fieldset->add(
array('type' => $typeSource,
'name' => $name,
'attributes' => array(
'id' => $name,
'class' => 'form-control ' . $cssStyles,
),
'options' => array('label' => _($label), 'format' => $dateformat)
)
);
$fieldInput = new \Zend\InputFilter\Input($name);
$fieldInput->setRequired($isRequired);
// handle the filters preferences of the attribute
if(!empty($filters)){
$filters = json_decode($filters, true);
foreach ($filters as $filter){
$filterChain->attachByName($filter);
}
}
$fieldInput->setFilterChain($filterChain);
$inputFilter->add($fieldInput);
}
$fieldset->setHydrator($customHydrator);
$this->add($fieldset);
$parentFilter->add($inputFilter, 'attributes'); // thanks to GeeH #zftalk irc
$this->setInputFilter($parentFilter);
return $this;
}
}
This is the Strategy class:
class DateTimeStrategy extends DefaultStrategy
{
/**
* Convert the database date field in the specific date format
*
* #see Zend\Stdlib\Hydrator\Strategy.StrategyInterface::extract()
*/
public function extract ($value)
{
$date = new \DateTime();
// Check the date in this format Y-m-d H:i:s
$validator = new \Zend\Validator\Date(array (
'format' => 'Y-m-d H:i:s',
'locale' => 'it'
));
if ($validator->isValid($value)) {
$thedate = $date->createFromFormat('Y-m-d H:i:s', $value);
$value = $thedate->format('d/m/Y H:i:s');
}
// Check the date in this format Y-m-d
$validator = new \Zend\Validator\Date(array (
'format' => 'Y-m-d',
'locale' => 'it'
));
if ($validator->isValid($value)) {
$thedate = $date->createFromFormat('Y-m-d', $value);
$value = $thedate->format('d/m/Y');
}
$pos = strpos($value, "-00");
if($pos !== false){
$thedate = $date->createFromFormat('Y-m-00', $value);
$value = $thedate->format('m/Y');
}
return $value;
}
/**
* Convert the date format from the post data array to the database format value
*
* #see Zend\Stdlib\Hydrator\Strategy.StrategyInterface::hydrate()
*/
public function hydrate ($value)
{
if (is_string($value) && "" === $value) {
$value = null;
} elseif (is_string($value)) {
$date = new \DateTime();
$validator = new \Zend\Validator\Date(array (
'format' => 'd/m/Y',
'locale' => 'it'
));
if ($validator->isValid($value)) {
$thedate = $date->createFromFormat('d/m/Y', $value);
$value = $thedate->format('Y-m-d');
}
if(strlen($value) == 7){
$thedate = $date->createFromFormat('m/Y', $value);
if($thedate){
$value = $thedate->format('Y-m-00');
}
}
}
return $value;
}
}
When I attach a Validator, for instance to the Datetime field, and then I post the data entered in the form, the result is this:
object(Product\Entity\Product)[917]
public 'id' => string '2' (length=1)
public 'uid' => null
public 'type_id' => string '1' (length=1)
public 'attribute_set_id' => string '1' (length=1)
public 'attributes' =>
array (size=12)
'description' => string 'test' (length=4)
'metadescription' => null
'metakeyword' => null
'name' => string 'hosting' (length=7)
'news_from_date' => string '29/06/2014' (length=10)
'news_to_date' => null
'price' => string '10.5' (length=4)
'short_description' => string 'test' (length=4)
'sku' => string 'hst-01' (length=6)
'special_price' => null
'status' => string '1' (length=1)
'urlkey' => string 'hosting' (length=7)
public 'createdat' => null
public 'updatedat' => null
As you can see the date has been posted without the correct format, ready to be inserted into the db table. The right date format must be yyyy-mm-dd.
This is the compiled form with all the attributes rendered.
Where is my error?

Find all objects with same property in array PHP

I am having a bit of difficulty testing one array against another.
I have a persons array
0 =>
object(Prospect)[196]
private 'firstname' => string 'Jane'
private 'surname' => string 'Doe'
private 'email' => string 'test#test.com'
private 'postcode' => string 'LS'
public 'region' => string 'Yorkshire'
1 =>
object(Prospect)[197]
private 'firstname' => string 'John'
private 'surname' => string 'Doe'
private 'email' => string 'test1#test.com'
private 'postcode' => string 'CH'
public 'region' => string 'Cheshire'
and a jobs array
0 =>
object(Job)[2]
private 'title' => string 'Job 1'
private 'ref' => string '0001'
private 'postcode' => string 'CH'
public 'region' => string 'Cheshire'
private 'wage' => string '£250'
1 =>
object(Job)[3]
private 'title' => string 'Job 2'
private 'ref' => string '0002'
private 'postcode' => string 'CH'
public 'region' => string 'Cheshire'
private 'wage' => string '£200.00'
What is the best way to iterate over each person in the persons array to find all jobs in the jobs array that have the same region for that particular person?
Edit: The data that I am working with is large on the prospect side and the job side, the above is just an example of the data that I am working with.
My overall goal for this is to be able to link each prospect with up to 6 jobs in the same region and then output this data into CSV file(s) to be used in a third party email campaign service.
I am quite new to this so would be grateful of any help
Thanks
Try this:
$persons_region = 'Cheshire';
$found_jobs = array();
foreach ($jobs as $job) {
if ($job['region'] == $persons_region) {
$found_jobs[] = $job;
}
}
the simpliest but maybe slowest way is
$job_ids = array();
foreach($persons_array as $person) {
foreach ($jobs_array as $id=>$job) {
if ($person->region == $job->region) $job_ids[] = $id;
}
}
so you can do
foreach ($job_ids as $id) {
// whatever using $id
}

Categories