Doctrine table IDs as array IDs - php

I found a very useful Doctrine function to set an attribute on a table for getting the database IDs also as keys in the resulting Doctrine_Collection. This function is documented here: http://www.doctrine-project.org/projects/orm/1.2/docs/manual/component-overview/en#collection:key-mapping
Now the question. I cannot use the table object itself, because I need to create a dynamic query on the table (and not the magic finders as in the example).
I tried this code:
$doctrineTable = Doctrine_Core::getTable($table);
$doctrineTable->setAttribute(Doctrine_Core::ATTR_COLL_KEY, "id");
$q = $doctrineTable->createQuery("t");
foreach ($filter as $c => $v) // lopp thru coumns
if (is_array($v)) // use whereIn if value is an array
$q->andWhereIn("t." . $c, $v);
elseif (is_null($v)) // use is null for null values
$q->andWhere("t." . $c . " IS NULL");
else // use where in other cases
$q->andWhere("t." . $c . "=?", $v);
return $q->fetchAll();
Unfortunately the resulting collection is still not using an associative array, but a normal one just using keys from 0 up.
Anybody has an idea how to achieve that for a query on a single table?
Cheers,
Daniel

You are looking for the INDEXBY keyword.
The INDEXBY keyword offers a way of mapping certain columns as collection / array keys. By default Doctrine indexes multiple elements to numerically indexed arrays / collections. The mapping starts from zero. In order to override this behavior you need to use INDEXBY keyword

Related

Comparing associative arrays inside foreach loop laravel PHP

Using eloquent, I am querying two sets of data like so:
$all_perms = Permission::all(); //all permissions
$role_perms = Auth::user()->roles()->permissions; //permissions belonging to a role
$role_perms is a subset of $all_perms and what I want is to loop both arrays and come out with a new array containing all permissions already assigned to a role together with those not yet assigned to a role.
What I have done is loop through both arrays in a foreach loop and if any one array belongs to both sets, I mark it by adding a check key with corresponding value 1 to the array so that I can identify is as a permission already assigned to a role.
foreach ($role_perms as $role_perm) {
foreach ($all_perms as $key => $value ) {
if (array_diff_assoc($all_perm, $role_perm)) {
$all_perm['check'] = 1;
}
}
}
but it keeps throwing the error:
array_diff_assoc(): Argument #1 is not an array
Are they better ways of doing this? Or what can I do on this one to make it work?
Thanks for any help
That's because it's a collection, not an array. If you want to get an array, try to use toArray():
$all_perms = Permission::all()->toArray();
Also, is this a typo here:
array_diff_assoc($all_perm, $role_perm);
It should be $all_perms
Try using the wonderful contains method that is available on all your collections:
foreach ($role_perms as $role_perm) {
if($all_perms->contains($role_perm))
{
// do whatever is needed
}
}
Checkout the docs for help with the contains method.

Propel - get primary key after find

The question boils down to finding the proper way how to getPrimaryKey when iterating over a yielded result. When using select method, the result is an object of ArrayCollection which doesn't provide the getPrimaryKey method. A simple snippet
$q = UserQuery::create();
$q->select('a', 'b'); //yields an ArrayCollection object, doesn't have getPrimaryKey method when iterated
$q->find();
However,
$q = UserQuery::create();
$q->find(); //yields an ObjectCollection object, has getPrimaryKey method when iterated
Update
I have tried to use the setFormater to force using the ObjectCollection. Ultimately, it resulted in exception being thrown.
$q = UserQuery::create()
->setFormater(ModelCriteria::FORMAT_OBJECT)
->select('a', 'b')
->find(); //Error populating object
Update 2
Providing an exact use case, since it may be unclear at first what I am looking for. I have a table with >100 columns. I am providing the functionality using behaviour to not disable (not select) some of them. Thus, I am unseting some of the columns, and basing the $q->select on the remaining ones.
if (!empty($tableQuery->tableColumnsDisable)) {
$columns = $tableQuery->getTableMap()->getColumns();
foreach ($columns as $index => $column) {
if (!empty($tableQuery->tableColumnsDisable[$column->getName()])) {
unset($columns[$index]);
continue;
}
$columns[$index] = $column->getName();
}
//TODO - returns array collection, object collection needed
$tableQuery->select($columns);
}
When using select(), Propel will skip object hydration and will just return an ArrayCollection containing an array for each result row.
To retrieve the id of each result row, you need to add the column name to the select(). You can then just retrieve the value from the row arrays by using the column name:
$users = UserQuery::create()
->select(['id', 'a', 'b'])
->orderBy('c')
->find();
foreach ($users as $user) {
$id = $user['id'];
}
The select functionality is described in the documentation and in the docblock of Propel\Runtime\ActiveQuery\ModelCriteria#select() (source).
When you are using Propel 1, the functionality is the same. Read more about it in the Propel 1 documentation or the docblock of ModelCriteria#select() (source).

PHP Iterator Custom Sort Order (Laravel 4)

I have a "Post" object, accessed via the IOC container. Various errors tell me this object's type ends up as a "Collection", which implements several interfaces, including IteratorAggregate and ArrayAccess.
I want to display a user-defined group of posts according to a specific order, e.g.:
$desired=array("69","63","70");//these represent post id's
Just sorting an array in this manner seems complex, but I want to sort my collection. I have been researching various combinations of usort(), uksort(), Eloquent's sortBy(), array_multisort()... but the most obvious solutions result in orders like 3,2,1 or 1,2,3, not 2,1,3.
The closest I have gotten to this goal is to fetch the ones I want,
//BlogController
private function myposts($desired){
$posts=$this->post->whereIn('id',$desired)->get();
...
"convert" the Collection object to an array,
$posts=$posts->toArray();
and treat the array with a custom function: source
function sortArrayByArray($array,$orderArray) {
$ordered = array();
foreach($orderArray as $key) {
if(array_key_exists($key,$array)) {
$ordered[$key] = $array[$key];
unset($array[$key]);
}
}
return $ordered + $array;
}
$sorted=sortArrayByArray($array1,$desired);
I can then vardump the array in the correct order, but since it is now an array, I can't access the $posts object in my view. Can I convert the array back into a post object?
This whole approach feels wasteful anyway, converting to an array and back... is it? Is there a more straightforward way of sorting the contents of a "Collection"?
This is a little better, perhaps (a native php function):
array_multisort($desired,$posts,SORT_STRING);
//only works with string keys
//$desire=(1,2,3) will not work!
Again, this works for arrays, but attempting directly on the "posts" object fails...
Finally, I discovered using a Presenter: https://github.com/robclancy/presenter#array-usage which works in the view, after one of the above is completed in the controller:
#foreach($posts as $post)
<?php $post=new PostPresenter($post) ?>
{{View::make('site.post.article')->with(compact('post'))}}
#endforeach
This finally works, but it still feels like a long way to do it. Is there a better way to accomplish this task? Performance concerns or best practices with one method vs. another? Thanks in advance for anyone able to help.
You can use the Collections own sort method and pass in a callback. The callback compares every two values in your collection tells the sort method which one is "higher". The sort method then sorts them accordingly. If you want a specific value order you just create mapping and sort by that. For more info check uasort
$posts->sort(function($a, $b){
$desired=array("69" => 0,"63" => 1,"70" =>2);
if(!array_key_exists($a,$desired) || !array_key_exists($b,$desired) || $a == $b){
return 0
}
else{
return ($desired[$a] < $desired[$b]) ? -1 : 1;
}
});
Alternatively to #tim's answer, you can re-assign the sorted array to a new Collection object:
$postsArray = $this->posts->toArray();
// Do some sorting/processing, then:
$newCollection = new \Illuminate\Database\Eloquent\Collection( $postsArray );

Reference an arrays key in its value

I know this seems like something that should be averted by design, but let's just say it is bitterly needed:
Is it possible to reference the key belonging to a value while it is being initialized?
Here is what I imagine it to be (not exactly the case in which I need it, but the key is primitive as well):
$array = array(25 => "My key is " . $this->key);
I need this because the array key is used in each value. Actually the value is another array which has a value in which the first array key is used. Like I said in the comments, I want to keep it DRY. Doing it is no problem, but I want to do it good ;)
If you are writing an array yourself you can just put key value to array value like:
$array = array(25 => "My key is 25");
If you are have an array already you can use a foreach and add all keys to it's values:
foreach($array as $key => $value) {
$array[$key] = sprintf('%s %s', $value, $key);
}
Or if you just want to have an array of keys of existing array you can use either array_flip if you want to maintain key=>value, but have keys and values flipped. Or you can use array_keys if you want just an array of keys.
To make what you want: initialize an array somewhere and do not add any keys to it's value you can implement ArrayAccess, Countable and have:
public function offsetGet($offset) {
return isset($this->container[$offset])
? $this->container[$offset] . ' ' . $offset
: null;
}
or something like this. But in this case you need to have a variable that contains this array to be an instance of your ArrayAccess implementation. And depending of usage of this class you probably will need to implement more interfaces.
No, there is no way to reference the key when defining the value. Except maybe writing a preprocessor that embeds it in the string. But that would only work for primitive values.

Use object property as array key

I have a PHP array of objects, say with two properties a and b. So for example I can do
$arr['a1']->a = $z;
$x = $arr['a1']->b;
The array is currently using the value of each object's a property as the array key, e.g.
$arr['a1']->a == 'a1'
This is so I can quickly look up the object by that property. I now need to quickly look up by b, and so want to switch the keys from being set to property a to being set to b (both are unique).
Is there an easy way to do this? In-place or into another array are both fine.
foreach($arr as $key => $object)
{
$arr2[$object->b] = $object;
}
This will create a new array that points to the same objects.
If you want them in one array, you can do as Joost suggested in the comments ($arr[$object->b] = $object; in the loop instead). However, that will only work if there are no duplicate keys between the two sets.

Categories