Hash::extract with HABTM - cakephp

I'm having difficulty understanding the syntax of Hash::extract when dealing with HABTM.
I have data coming back from a find() that looks like this:
array(
(int) 0 => array(
'EventsGroup' => array(
'id' => '34',
'event_id' => '5',
'group_id' => '1'
)
),
(int) 1 => array(
'EventsGroup' => array(
'id' => '29',
'event_id' => '2',
'group_id' => '1'
)
)
)
I'm trying to get to an array that looks like: array(x,y,z) where x,y,z are event_id's.
The Cake documentation's example looks like:
$users = $this->User->find("all");
$results = Hash::extract($users, '{n}.User.id');
Based on that, I tried:
$eventsGroups = $this->EventsGroup->findAllByGroupId($groupid);
$secEvents = Hash::extract($eventsGroups, '{n}.{EventsGroup}.event_id' );
$secEvents2 = Hash::extract($eventsGroups, '{n}.EventsGroup.event_id' );
$secEvents3 = Hash::extract($eventsGroups, '{n}.[text=EventsGroup].event_id);
None of which worked.
I found a way to get what I wanted without using Hash::extract, but I'd like to use it as some of the other methods will be useful to me down the road.
Any help or pointers would be greatly appreciated!
Thanks

Have you tried:
$eventsGroups = $this->EventsGroup->findAllByGroupId($groupid);
$secEvents = Hash::extract($eventsGroups, '{n}.EventsGroup[event_id]' );
As the docs says to retrieve a field you should use a matcher and not a expression. And the brackets ({}) are not needed, they are used to build expressions like '{n}.{s}'...

Related

CakePHP - select from database with condition

I have this selector:
$this->Table->find('list',array('contain'=>false,'conditions'=>array('status'=>1,'unit_id'=>null,'country_id'=>$countryId,'eday'=>$eday),'fields'=>'id'));
And this works perfect. But now i need to have another one i cant find how to do this ;)
I need to select all records from Table but with condition:
'eday'>=$eday AND 'eday'<$eday+7
Its this posibble in easy way? Maybe this is stupid question but i dont have exp in PHP ;)
In cakephp the equivalent for and condition is
Example: column1 = 'value' AND column2 = 'value'
'AND' => array(
'column1' => 'value',
'column2' => 'value'
)
So your query builder would be like this
$this->Table->find('list',array(
'contain' => false,
'conditions' => array(
'status' => 1,
'unit_id' => null,
'country_id' => $country,
'AND' => array(
'eday >=' => $eday,
'eday <' => ($eday+7)
)
),
'fields'=>'id'
));
Just pass an AND array with in your current conditions array :
$this->Table->find('list',array('contain'=>false, 'conditions'=>array('status'=>1,'unit_id'=>null,'country_id'=>$countryId,'eday'=>$eday, AND => array( array('eday >=' => $eday) , array( 'eday <' => $eday+7) )), 'fields'=>'id' ));
Have you tried something like this :
$conditions['status'] = 1;
$conditions['unit_id'] = null;
$conditions['country_id'] = $countryId;
$conditions['eday >='] = $eday;
$conditions['eday <'] = $eday + 7;
$this->Table
->find('list') //either use this
->find('all') //or use this
->find() //or this
->where($conditions)
->select(['addFiledsToSelectHere'])
This looks more cleaner.

CakePHP get model structure in controller action

I want to get the current model's structure from the controller, similar to the return of $this->modelName->read(null, id), but without the actual data in the record, just the structure.
Is this something Cake has built in?
I do not know of any such thing, although you can call, $this->ModelName->schema(); which will give output like:
array(
'id' => array(
'type' => 'integer',
'null' => false,
'default' => null,
'length' => (int) 11,
'key' => 'primary'
)
);
So you could use that to write something on your own like:
$schema = $this->Model->schema();
$values = array_fill ( 0 , count($schema), '' );
$model = array('Model' => array_combine(array_keys($schema), $values));

How to access a related table field from a Controller?

I am trying to get an array structure of a database and a few of its fields from my controller.
The fields I want are: Document.id, Document.name, Document.submission_date, and the Requester.name which is joined with: Requester.id = Document.requester_id
In my Controller:
Method A:
$documents = $this->Document->find('list', array('fields' => array('Document.name', 'Requester.name', 'Document.submission_date')));
Can't seem to find 'Requester.name'
Method B:
$documents = $this->Document->find('list', array('fields' => array('Document.name', 'Requester.name', 'Document.submission_date'), 'recursive' => 0));
Gives me:
array(
'2012-08-17' => array(
'Document_A' => 'Requester_Z'
),
'2012-08-05' => array(
'Document_B' => 'Requester_Y'
),
'2012-07-09' => array(
'Document_C' => 'Requester_X'
)
)
But I need it to be in format:
array(
(int) 0 => array(
'id' => '16'
'submission_date' => '2012-08-17'
'name' => 'Document_A',
'requester_name' => 'Requester_Z'
),
(int) 1 => array(
'id' => '41'
'submission_date' => '2012-08-05'
'name' => 'Document_B',
'requester_name' => 'Requester_Y'
),
(int) 2 => array(
'id' => '213'
'submission_date' => '2012-07-09'
'name' => 'Document_C',
'requester_name' => 'Requester_X'
),
)
I can't seem to figure it out after going through the 2.0 CakeBook and on StackOverflow...
Any help would be appreciated? Sorry - I'm still a n00b with CakePHP (but REALLY loving it so far!) Thanks!
You need to do a find('all') instead of a find('list') and you can use the fields key like you already are to limit the amount of info that's returned.
It won't come back exactly as you need the data, each field will be in a key of the model it belongs to - if you really need it in that format you can get away with using a virtual field, a custom find, or an afterFind callback to modify the data.
Find list is generally for an id => label formatted array.
You can try this:
$documents = $this
->Document-
>find('all',
array(
'fields' => array('Document.name', 'Requester.name', 'Document.submission_date'),
'joins' => array(
'table' => 'requesters',
'alias' => 'Requester',
'type' => 'left',
'conditions' => array('Requester.id = Document.requester_id')
)
)
);

sum() function in cakephp query

I am using this query, but it is not returning ctotal. Please help.
$total = $this->RequestedItem->find('all',
[
'sum(cost * quantity) AS ctotal',
'conditions' => [
'RequestedItem.purchase_request_id' => $_GET['po_id']
]
]
);
You should not be using PHP superglobals directly in CakePHP. You should instead use Model.field naming so that you do not get ambiguous field errors.
Virtual fields is the way to go but that is not your problem, you need to read the book some more.
$total = $this->RequestedItem->find('all', array(array('fields' => array('sum(Model.cost * Model.quantity) AS ctotal'), 'conditions'=>array('RequestedItem.purchase_request_id'=>$this->params['named']['po_id'])));
should work fine, with the virtualFields it would be
var $virtualFields = array('total' => 'SUM(Model.cost * Model.quantity)');
$total = $this->RequestedItem->find('all', array(array('fields' => array('total'), 'conditions'=>array('RequestedItem.purchase_request_id'=>$this->params['named']['po_id'])));
Fields go in the 'fields' key, just like conditions go in the 'conditions' key. See http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find
This works too, worked fine for me
$sum = $this->Modelname->find('all', array(
'conditions' => array(
'Modelname.fieldname' => $conditions),
'fields' => array('sum(Modelname.fieldname) as total_sum'
)
)
);
Temporarily set the virtualFields prior to doing a find.
$this->MaterialScan->virtualFields = array(
'total_qty' => 'COUNT(MaterialScan.id)',
'total_lbs' => 'SUM(MaterialScan.weight)'
);
$materialScans = $this->MaterialScan->find('all',array(
'conditions' => array(
'MaterialScan.id' => $scans
),
'group' => array('MaterialScan.part_number')
));
This avoids having the [0] elements in the returned array.
You can use virtualFields:
var $virtualFields = array(
'the_sum' => 'SUM(Model.cost * Model.quantity)'
);

cakephp find list

Hi I want to be able to generate a list using find so that I can use in select helper. but there is a problem. i want too fetch id,name(first + last). so how can I achieve it. I want first_name and last_name to be joined as name . How can I achieve it.
$this->User->find('all',array('fields' => array('first_name','last_name','id')));
I can't use model filters and callback Please suggest me how can I do it in controllers itself.
I think this can be done using the virtualFields and displayField properties in your model.
In your model, define a virtual field for the full name like this:
public $virtualFields = array(
'full_name' => 'CONCAT(User.first_name, " ", User.last_name)'
);
If you now set displayField to full_name you should be able to get a list of your users with the $this->User->find('list') method which you can use without problems with the Form-helper.
public $displayField = 'full_name';
... or:
public $displayField = 'User.full_name';
The id is fetched automatically.
Another solution is to use Cake's Set::combine to build what you need...
$users = $this->User->find('all',array('fields' => array('first_name','last_name','id')));
$user_list = Set::combine($users, '{n}.User.id', array('{0} {1}', '{n}.User.first_name', '{n}.User.last_name'));
Result will look something like:
array(
[2] => 'First Last',
[5] => 'Bob Jones'
)
Here's the documentation link:
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::combine
To achieve this first go to the model and add this line
public $virtualFields = array('full_name' => 'CONCAT(first_name, " ", last_name)');
and then go to controller file just use the name "full_name" which you put in virtual fields
$this->User->find('all',array('fields' => array('full_name','id')));
It returns name with combined fields
+1 on Tim's answer, however, if you need an alternative, teknoid wrote a nice article about this a long time ago:
http://nuts-and-bolts-of-cakephp.com/2008/09/04/findlist-with-three-or-combined-fields/
In my case, Set::combine was the way to go, since I had to deal with concatenation of fields in associated models, like:
$bancos_enteros = $this->Financiacion->Banco->find('all', array(
'fields' => array('Empresa.codigo_contable','Empresa.nombre_corto', 'Banco.id'),
'order' => array('Empresa.codigo_contable' => 'asc'),
'recursive' => 1
));
$bancos = Set::combine(
$bancos_enteros,
'{n}.Banco.id',
array(
'{0} {1}',
'{n}.Empresa.codigo_contable',
'{n}.Empresa.nombre_corto'
)
);
returning
array(
(int) 14 => '57200002 Caixa',
(int) 15 => '57200003 Sabadell',
(int) 3 => '57200005 BBVA',
(int) 16 => '57200006 Deutsche Bank',
(int) 17 => '57200007 Popular',
(int) 18 => '57200009 March',
(int) 26 => '57200010 Bankinter',
(int) 4 => '57200011 Santander'
)
While
$this->Financiacion->Banco->Empresa->virtualFields = array(
'codigo_nombre' => 'CONCAT(Empresa.codigo_contable,Empresa.nombre_corto)'
);
$this->Financiacion->Banco->virtualFields['codigo_nombre'] = $this->Financiacion->Banco->Empresa->virtualFields['codigo_nombre'];
$bancos = $this->Financiacion->Banco->find('list', array(
'fields' => array('Banco.id','Banco.codigo_nombre'),
'order' => array('Banco.codigo_nombre' => 'asc'),
'recursive' => 1
)
);
returns a SQL error in a following query if I don't delete the virtual fields first:
unset($this->Financiacion->Banco->Empresa->virtualFields);
unset($this->Financiacion->Banco->virtualFields);

Resources