Pimcore - get old Object state in preUpdateObject hook - php

I need to compare if a certain property of my object has changed when someone is saving it. I wrote a plugin to be able to add some functionality before and after updating an object in the backend happens.
So, I don't know if this is not working as expected or if I'm getting this wrong.
I thought I would get the state before it is saved to the database in:
function preUpdateObject(Object_MyObject $object) {}
And the new state of the object in
function postUpdateObject(Object_MyObject $object) {}
But this doesn't work:
public function preUpdateObject(Object_MyObject $object) {
$this->tempOldDate = $object->getUpdate();
}
public function postUpdateObject(Object_MyObject $object){
if($this->tempOldDate->compareDate($object->getUpdate()) == -1) {
// do something because a newer date has been entered
}
}
Any clue how I can get the old object state BEFORE it is updated??

Seems like this function is not working as expected. I filed a bug report:
http://www.pimcore.org/issues/browse/PIMCORE-1232
I created a workaround that can be used.
The database is NOT updated. So it's correct that preUpdateObject is the function to use to get the object state, but:
Check that database is not updated.... I want a date from my object that sits in table 'object_1'
// oldDate has the old value
$dbAdapter = Pimcore_Resource_Mysql::get("database");
$dbentry = $dbAdapter->fetchRow(
$dbAdapter->select()
->from('object_1')
->where('o_id = ?', $object->getId()));
$oldDate = new Pimcore_Date($dbentry['update']);
Using the generated Object class doesn't work, but if you clear the cache it does
// get's the new date from the editor
$oldDate = Object_MyObject::getById($object->getId())->getUpdate();
// this also works but don't know if it is safe to delete
// the object from the registry
Zend_Registry::set('object_' . $object->getId(), false);
$oldDate = Object_MyObject::getById($object->getId())->getUpdate();

Related

dissociated variables in array

I have a problem with php variables. I use a code that looks like this:
for($i = 0; $i <= $nbRecurrence; $i++) {
$res = new Reservation();
$res->setDateDebut($DateDebut->add(new \DateInterval('P1D')));
$res->setDateFin($DateFin->add(new \DateInterval('P1D')));
$lesRes[] = $res;
$this->app['orm.ems']['gestionReservationAuto']->persist($res);
$this->app['orm.ems']['gestionReservationAuto']->flush();
}
The problem is that although I adds each element in the array, but when I use a var_dump for analysis, all $res in $lesRes are identical. Registered data is yet different in the database ...
How can I do to have an array with $res that do not like?
(if I make a request to have the items I just added in database, I have the same problem, I have an array of x elements $res, which are all identical.)
I guess $DateDebut is a DateTime object.
I also guess that Reservation::setDateDebut() looks something like:
class Reservation
{
private $dateDebut;
public function setDateDebut(DateTime $dateDebut)
{
$this->dateDebut = $dateDebut;
}
}
And let's write again the code that uses it:
$res = new Reservation();
$res->setDateDebut($DateDebut->add(new \DateInterval('P1D')));
What you miss is the fact that DateTime::add() does not create a new DateTime object but returns a reference to the current object (i.e. return $this;).
This means on each iteration you change the value of object $DateDebut then you pass it to Reservation::setDateDebut() which also doesn't make a copy of it but just links to the object it gets as argument.
After the loop you still have only two instances of DateTime; one of them is accessible through the variable $DateDebut and the members $dateDebut of all the Reservation objects created during the loop. The other instance is $DateFin and the same discussion is valid for it too.
Your code is a victim of variables aliasing.
How to fix it:
You need to create copies of $DateDebut somewhere, either in the loop code (and pass the copies to Reservation::setDateDebut()) or in the body of Reservation::setDateDebut():
// Either
$res->setDateDebut(clone $DateDebut->add(new \DateInterval('P1D')));
// Or
public function setDateDebut(DateTime $dateDebut)
{
$this->dateDebut = clone $dateDebut;
}
You decide where is the most appropriate place to do it, depending how the rest of the code works with these objects.
Because you change the value of $DateDebut in the loop you should make copies of the object there.
If the class Reservation changes the value of its $dateDebut member then you should (also) clone it in the setter method. This is because the caller of Reservation::setDateDebut() does not expect the Reservation class make changes on the value it passes as an argument.

PHP - Editing object clone affects original object

So I have an object, let's call it Example.
class Example {
public function __construct ($id) {
$this->Id = $id;
}
}
$objectA = new Example(10);
It has an id (it pulls this from somewhere), the goal of the object is to overwrite a similar object with this object's properties (post to an external service). This is done by simply changing the ID of the object, and running an ->update() method. However, it must first change its ID (among other properties) to match the ids of object B.
So what I do is clone the current object, reassign the needed properties, and then pass that cloned object to the update method, so that the update method uses the $post values for the update.
public function createPost ($id) {
$post = clone $this;
$post->Id = $id;
return $post;
}
$objectA->update($objectA->createPost(12));
$objectA->update($objectA->createPost(16));
$objectA->update($objectA->createPost(21));
The issue I'm having is this object A needs to be used for multiple different updates, and it uses the ID it is originally assigned as a pointer to what IDs it must later use for $post, and in this scenario, the value of $this->ID is getting reassigned to the $id that is passed in ->setParameters(), even though I'm trying to assign it to a clone of $this, rather than $this itself.
My impression is that $objectA = $objectB assigns ObjectB to the same pointer that points to ObjectA, but that "clone" was supposed to actually make a copy of that object, so that if properties of the clone are changed, the original object remains unaffected, but that doesn't seem to be the case here. Is there a particular method I should instead be using to ensure that the original object's value aren't ever changed when a clone of it is?
I think what you want is a deep clone. Take a look at this link.
The issue I ran into here was a shallow cloning issue it would appear.
Forget what I referenced above, the problem was more so this: -
class Example {
public function __construct ($id) {
$this->ObjId = (object)array("Id" => 5);
$this->Id = $id;
}
}
$example = new Example (5);
$clone = clone $example;
$clone->ObjId->Id = 10;
In this example, I was trying to change the ->Id of a standard php object that was stored within my main object (I had used json_encode to pull a whole structure of objects) that was called "Id". The result of the above was this: -
echo $example->ObjId->Id; //10
echo $example->Id; //5
echo $clone->ObjId->Id; //10
echo $clone->Id; //5
So I had to write this method as part of the Example class: -
public function __clone () {
foreach ($this as $key => $val) {
if (is_object($val) || (is_array($val))) {
$this->{$key} = unserialize(serialize($val));
}
}
}
So now, the result was this: -
echo $example->ObjId->Id; //5
echo $example->Id; //5
echo $clone->ObjId->Id; //10
echo $clone->Id; //5
This is because when I cloned the main Example object, it was creating an new variable for it's properties, but if one of it's properties was an object or array, it was copying the address of that object or array for the new cloned object. The above method prevents that, and does a "deep" copy.

Concrete5 MVC parent::save() having trouble finding database insert function, how to save to database

I am editing a concrete5 add-on and am trying to figure out how the program is saving values to the database. The following function is where the database save is happening but I am not sure where the "parent::save()" function is.
protected function SaveRecord() {
$func = 'jso'.'n_encode';
$this->errors = is_array($this->errors) ? $func($this->errors) : $this->errors;
$this->effectiveDate = is_numeric($this->effectiveDate) ? date('Y-m-d', $this->effectiveDate) : $this->effectiveDate;
$this->expirationDate = is_numeric($this->expirationDate) ? date('Y-m-d', $this->expirationDate) : $this->expirationDate;
//var_dump($this); die();
parent::Save();
// a bit hacky, but we are saving the errors as JSON, and we might need to access them later.
$this->errors = (array) json_decode($this->errors);
}
I have followed the class up to its parent and it does not have a save function. I followed the parent up to its parent until I found a save function in the "adodb" class, but die() never happens when put in this function. Please help me figure out how I am supposed to save values in Concrete5 to the database! (More of my code at: https://stackoverflow.com/questions/26940176/concrete5-add-on-extension-save-value-to-database).
#CaitlinHavener Your SaveRecord method should be like this.
public function SaveRecord($data){
$data['my_array'] = serialize($data['my_array']);
parent::save($data);
}
refer this link concrete5 document

Dynamically create object properties, got: "Strict standards: Creating default object from empty value"

I'm trying to dynamically create object properties for JSON representation of an object. The class User will feature some default properties (setted in __construct). I'm using custom object instead of arrays because i prefer object oriented style (and i need also custom setter/getters methods).
However the first try gives me:
Strict standards: Creating default object from empty value.
even if the code actually works (and json_encode shows the right output):
<?php
class User
{
protected $data = array();
public function __set($property, $value)
{
$this->data[$property] = $value;
}
}
$u = new User();
$u->name = "James Smith"; // Works
$u->status->active = false; // Fail
$u->status->modified = time();
var_dump(json_encode($u));
?>
I think it's because the call $u->status->active, when property $u->status does not exist yet. Do you know how to fix this?
OK I sorted that out for you :) It was interesting.
First, you have not initialized the status property. So in theory, this should have been sufficient:
$u->status = new StdClass;
However, it is more complicated than this. Even if you do it, it won't work. That is because you are setting your fields in the data array, but you are never GETTING THEM OUT from there!
So when you access a field ($u->status) you are NOT taking the field you have just set: you are accessing an unset object property. If you try to print $u->name after setting it, you will not get anything, because you have not created a getter function which would read your data array.
You should either create a getter, or delete the setter (it will work anyway, but may not be what you need).
If you comment out the setter, everything works without warnings. See this simplified version:
<?php
error_reporting(E_STRICT);
class User
{
}
$u = new User();
$u->name = "James Smith";
$u->status = new StdClass; // Comment this line and you will get the strict warning
$u->status->active = false;
var_dump($u);

PHP custom object casting

I have a custom class object in PHP named product:
final class product
{
public $id;
public $Name;
public $ProductType;
public $Category;
public $Description;
public $ProductCode;
}
When passing an object of this class to my Data Access Layer I need to cast the object passed into a type of the product class so I can speak to the properties within that function. Since type casting in PHP works only with basic types what is the best solution to cast that passed object?
final class productDAL
{
public function GetItem($id)
{
$mySqlConnection = mysql_connect('localhost', 'username', 'password');
if (!$mySqlConnection) { trigger_error('Cannot connect to MySql Server!'); return; }
mysql_select_db('databaseName');
$rs = mysql_query("SELECT * FROM tblproduct WHERE ID='$id';");
$returnObject = mysql_fetch_object($rs, 'product');
return $returnObject;
}
public function SaveItem($objectToSave, $newProduct = false)
{
$productObject = new product();
$productObject = $objectToSave;
echo($objectToSave->Name);
$objectToSave->ID;
}
}
Right now I am creating a new object cast as a type of product and then setting it equal to the object passed to the function. Is there a better way of accomplishing this task? Am I going about the wrong way?
EDITED FOR CLARITY - ADD FULL PRODCUTDAL CLASS
You don't need to cast the object, you can just use it as if it was a product.
$name = $objectToSave->Name;
I´m not sure what you are trying to achieve, but if $objectToSave is already of class product:
You can simply call $objectToSave->SaveItem() (assuming SaveItem() is part of the product class) and access it´s properties in the function like $this->Name, etc.;
In your code $productObject and $objectToSave will hold a reference to the same object.
Type casts in PHP are done like this:
$converted = (type) $from;
Note, that this won't work if the object types are not compatible (if for example $form happens to be a string or object of mismatching type).
But usual solution (called Active Record pattern, present for example in Zend Framework) is to have a base class for a database item called Row. Individual items (for example the class product from your sample) then inherit from this class.
Typical ZF scenario:
$table = new Product_Table();
$product = $table->find($productId); // load the product with $productId from DB
$product->someProperty = $newPropertyValue;
$product->Save(); // UPDATE the database
Which is IMO much better than your solution.
EDIT:
You can't cast between two unrelated objects, it is not possible.
If you want to use the DAL like this, skip the "product" object and go for simple associative array. You can enumerate over its members with foreach, unlike object's properties (you could use reflection, but that's overkill).
My recommendation: Go for the Active Record pattern (it is easy to implement with magic methods). It will save you a lot of trouble.
Currently, you are creating a new Product, then discarding it immediately (as its reference is replaced by $objectToSave.) You will need to copy its properties one by one, I regret.
foreach (get_object_vars($objectToSave) as $key => $value)
{
$product->$key = $value;
}
(If the properties of $objectToSave are private, you will need to a expose a method to_array() that calls get_object_vars($this).)

Categories