Copy a Doctrine object with all relations - php

I want to copy a record with all his relations.
I'm trying with:
$o = Doctrine::getTable('Table')->Find(x);
$copy = $object->copy();
$relations = $o->getRelations();
foreach ($relations as $name => $relation) {
$copy->$relation = $object->$relation->copy();
}
$copy->save();
This code doesn't works, but I think it's on the way.

I never could get the deep copy function to operate correctly.
I manually coded a deep copy function for one of my models like this
public function copyAndSave ()
{
$filters = array('id', 'created');
$survey = $this->copy();
$survey->Survey_Entries = new Doctrine_Collection("Survey_Model_Entry");
$survey->Assignment_Assignments = new Doctrine_Collection("Assignment_Model_Assignment");
$survey->Survey_Questions = new Doctrine_Collection("Survey_Model_Question");
$survey->save();
foreach ($this->Survey_Questions as $question)
{
$answers = $question->Survey_Answers;
$newQuestion = $question->copy();
$newQuestion->survey_surveys_id = $survey->id;
$newQuestion->save();
$newAnswers = new Doctrine_Collection("Survey_Model_Answer");
foreach($answers as $answer)
{
$answer = $answer->copy();
$answer->save();
$answer->survey_questions_id = $newQuestion->id;
$newAnswers->add($answer);
}
$newQuestion->Survey_Answers = $newAnswers;
$survey->Survey_Questions->add($newQuestion);
}
return $survey->save();
}

You can read about copy() here. It takes an optional parameter $deep:
$deep
whether to duplicates the objects targeted by the relations
So
$copy = $object->copy(true);
should do it.

Sorry if I'm resurrecting this thread...
I found myself in search of a solution recently where I needed to copy a record and retain the references of the original. A deep copy $record->copy(true) copies the references, which was no good for me. This was my solution:
$record = Doctrine_Core::getTable('Foo')->find(1);
$copy = $record->copy();
foreach($record->getTable()->getRelations() as $relation) {
if ($relation instanceof Doctrine_Relation_Association) {
$ids = array();
foreach ($relation->fetchRelatedFor($record) as $r) {
$ids[] = $r->getId();
}
$copy->link($relation->getAlias(), $ids);
}
}
if ($copy->isValid()) {
$copy->save();
}
Hope this helps :)

This is how i done, but some fix is needed.
$table = $entidade->getTable();
$relations = $table->getRelations();
foreach($relations as $relation => $data) {
try {
$entity->loadReference($relation);
} catch(Exception $e) {
die($e->getMessage());
}
}

I am using Symfony1.4.1 and that uses Doctrine 1.2.1 (I think).
I have been trying to make a function that did all the above myself, when I found one that already exists.
Try this in any function and look at the results:
$tmp=$this->toArray(TRUE);
var_dump($tmp);
$this->refreshRelated();
$tmp=$this->toArray();
var_dump($tmp);
$tmp=$this->toArray(TRUE);
var_dump($tmp);
exit();
I am going to try two different things:
A/ put $this->refreshRelated() into the constructor of all my model objects.
B/ write a function that takes an array depicting the object graph that I want populated. Calling the function refereshRelatedGraph($objectGraphArray). With the right structure of the array (having all the appropriate relation names at each level), I could control which relations get populated and which don't. One use for this is to populate only children, not parent relations. The other is for when a ERD/Schema/ObjectGraph has an element that is 'owned' by more than one object (many to many, other special circumstances that I have), I could control which side of the relationships get pre(non lazy) loaded.

Related

How to create single array of id's from parent and child array

I'm Trying to create single array that contains all the ID of parent and child from the database.
But all I was getting is single data.
My ideal output is:
array('160', '161', '162', '163', '164');
what am I getting is only
array('160');
Here is what I've done so far.
public function arrayId(array $elements) {
$where_in = array();
foreach($elements as $element){
if($element->isArray) {
$elems = $this->my_model->select_where('tbl_policies', array('parent_id' => $element->id));
$this->arrayId($elems);
}
$where_in[] = $element->id;
}
return $where_in;
}
$id = 160; //for instance
$elements = $this->my_model->select_where('tbl_policies', array('id' => $id));
$where_in = $this->arrayId($elements);
die(print_r($where_in));
and the data I'm fetching here:
tbl_policies
It's kinda difficult for me to construct questions. So please if something is not clear, do comment below, I'll try my best to make it more understandable. Thanks in advance.
I understand, that you want to delete a parent with all its children and grandchildren. But you do it not directly and sequentially rather want to collect all ids of the records to be deleted. You should go following steps:
Parent-Id (example 160) is already known. Add this to your list.
Write a recursive function such as getChildrenIds(parentId).
Within this function you should iterate over children. And if a child has the flag "isArray" (according to your application logic) then you should call getChildrenIds(currentChildId)
I have written following function. It should work.
public function getChildrenIds( int $parentId, array &$idList) {
$idList[] = $parentId;
$records = $this->my_model->select_where('tbl_policies', array('parent_id' => $parentId));
foreach($records as $r){
if($r->isArray)
$this->getChildrenIds($r->id, $idList);
else
$idList[] = $r->id;
}
return;
}
public function CollectIds(){
$id = 160; //for instance
$where_in = array();
$this->getChildrenIds($id, $where_in);
}
Please notice, that $where_in passed by reference to the recursive function getChildrenIds() and filled there.

iterating through Doctrine's changeSet

I am attempting to log specific actions users are taking on my site and have a listener check if certain entities are being updated, and if so, my goal is to log the fields they are editing, but not all the fields (some are not important or too long).
I have a problem saving the changeset to my database which is why I want to filter for important fields. This works to save the changeset, but when there are several nested arrays within the changeset, the array is not saved correctly (it cuts off after 3 or so arrays within arrays). I am using the array type in postgres. In my postupdate event I have:
if ($entity instanceof ListingQuery) {
$entityManager = $eventArgs->getEntityManager();
$ul = new UserLog();
$uow = $entityManager->getUnitOfWork();
$changeset = $uow->getEntityChangeSet($entity);
$ul = new UserLog();
$ul->setLog($changeset);
$ul->setUser($entity->getUser());
$entityManager->persist($ul);
$entityManager->flush();
}
I've been looking over the docs, but am not really sure how to iterate over the $changeset. It's a multidimension array that can have a variable amount of arrays within based on the number of fields updated. Userlog is a simple entity I have for saving the $changeset and the log field is an array.
I created a function that takes the $changeset and loops through the first three levels of the array, but its not saving the name of the field and only saves the values before and after. How do I access the field names changed in the $changeset?
I think I have a solution that works well. It adds the entity type so it does not match the changeset exactly from Doctrine2, but I think works for my purpose. I found a bunch of other posts form people trying to log specific changes in Doctrine with mixed results so please post if anyone else has a better solution.
public function looparray($arr, $type) {
$recordset[] = array($type);
$keys[] = array_keys($arr);
foreach ($keys as $key) {
if (!is_array($key)) {
if (array_key_exists($key, $arr)) {
$recordset[] = array($key => $arr[$key]);
}
} else {
foreach ($key as $key1) {
if (!is_array([$key1])) {
$recordset[] = array($key1 => $arr[$key1]);
} else {
if (!is_array([$key1])) {
$recordset[] = array($key1 => $arr[$key1]);
} else {
$recordset[] = array($key1 . ' changed ' => $key1);
}
}
}
}
}
return $recordset;
}

Kohana ORM - has many through models stored in parent model

I had many problems with has_many-through relationships but finally I found nice example here which solved most of my problems. However, according to code presented below I have couple questions.
firstly, code:
$artists = ORM::factory('artist')->find_all();
foreach ( $artists as $artist )
{
foreach ( $artist->media->find_all() as $m )
{
echo $m->name;
}
}
1) This example is probably controller. What if I want to store media in $artists to send one variable to view? Is it possible to store media as media property in artist object? (I mean for example $artists[0]->media[0]->name)
2) Is it possible to completely load $artists without this loop?
1) If I understood correctly, you need to get some element from medias
$artists = ORM::factory('artist')->find_all()->as_array();
$media = $artists[0]->media->find_all()->as_array(); // media of first artist
$name = $media[0]->name;
2) See above $artists is an array of ORM objects
Following my comment, this is what I'd do.
class Model_Artist extends ORM {
///
/// Whatever you have now
///
private $_media_cache = NULL;
public function media($key = NULL)
{
// Check cache
if($this->_media_cache == NULL)
{
$this->_media_cache = $this->media->find_all();
}
if($key !== NULL)
{
// Use Arr::get in case index does not exist
// Return empty media object when it does not exist so you can
// 'harmlessly' ask for its properties
return Arr::get($this->_media_cache, $key, ORM::factory('Media'));
}
return $this->_media_cache;
}
}
Callable as
$artists[0]->media(0)->name

rackspace cloudfiles api - most efficient method to return container files

Using the Rackspace CloudFiles API (in PHP), there are times when I need to get just a list of the all the current files in the container. What I just came up with is terribly slow and in-efficient because it gets every object pertaining to that file. So what I have:
My Function
function clean_cdn() {
$objects = $this->CI->cfiles->get_objects();
foreach ($objects as $object) {
echo $object->name;
}
}
get_objects wrapper for CodeIgniter
public function get_objects() {
$my_container = $this->container_info();
try {
return $my_container->get_objects(0, NULL, NULL, NULL);
} catch(Exception $e) {
$this->_handle_error($e);
return FALSE;
}
}
cloudfiles get_objects function
function get_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL)
{
list($status, $reason, $obj_array) =
$this->cfs_http->get_objects($this->name, $limit,
$marker, $prefix, $path);
if ($status < 200 || $status > 299) {
throw new InvalidResponseException(
"Invalid response (".$status."): ".$this->cfs_http->get_error());
}
$objects = array();
foreach ($obj_array as $obj) {
$tmp = new CF_Object($this, $obj["name"], False, True);
$tmp->content_type = $obj["content_type"];
$tmp->content_length = (float) $obj["bytes"];
$tmp->set_etag($obj["hash"]);
$tmp->last_modified = $obj["last_modified"];
$objects[] = $tmp;
}
return $objects;
}
This will give me just the name (which is all I need for what I'm doing currently) but is there a better way?
Update
I noticed that I could technically just put all the "directories" in an array and iterate over them in a foreach loop, listing each of them as the 4th parameter of get_objects. So get_objects(0, NULL, NULL, 'css'), etc. Still seems like there's a better way though.
If you are using the old php-cloudfiles bindings, use the list_objects() method. This will just return a list of the objects in the container.
php-cloudfiles bindings are deprecated now, the new official php cloudfiles bindings are php-opencloud (object-store) and you can find the section on listing objects in a container here
Using php-opencloud, if you have a Container object, use the ObjectList() method to return a list of objects:
$list = $container->ObjectList();
while ($obj = $list->Next()) {
// do stuff with $obj
}
The $obj has all of the metadata associated with the object that's returned by the list (which is to say, there are certain attributes that can only be retrieved by invoking the object directly, but this should have most of what you need).

Static method, Zend_View error

I have a static method 'findAll' on a model which basically gets all rows with certain criteria. This method works fine and I can call it using:
$m::findAll();
Where $m is the model name as a variable. I can output this and it returns correct results. However, when assigning this to a variable in the Zend_View object, as:
$this->view->viewvariable = $m::findAll();
I get the error:
Zend_Db_Table_Exception: Too many
columns for the primary key
Any ideas why?
Find all function:
final public static function findAll($where = false, array $options = array()) {
$object = new static();
if (!empty($options)) $options = array_merge($object->options, $options);
else $options = $object->options;
$run = $object->buildDefaultSelect($where, $options);
$rows = $run->fetchAll();
if ($options['asObject'] == true) {
$result = array();
foreach ($rows as $r) {
$class = new static();
$class->setInfo($r);
$result[] = $class;
}
return $result;
} else {
if (count($rows) > 0) return $rows;
else return array();
}
}
Note: This function works fine everywhere apart from when assigning to a view variable. If I run the below (not assigning it to a view variable), it shows the correct array data.
var_dump($m::findAll($module['where'], $module['options']));
exit;
In my view (I have replaced the actual name with viewvariable for the sake of this post):
<?php foreach($this->viewvariable as $item) { ?>
//Do some echoing of data in $item
//Close foreach
I doubt the issue is with Zend_View. It's hard to tell without seeing your code, but my guess is that findAll() is using the Zend_Table_Db find() function incorrectly.
To my knowledge, the only place that throws that exception is the find() function on Zend_Db_Table_Abstract.
Perhaps, inside the findAll() function (or in a function it calls) you're doing one of these:
$zendDbTable->find(1,2) //is looking for a compound key
$zendDbTable->find(array(1,2)) //is looking for two rows
When you really want the opposite.

Categories