CakePHP HABTM recursive issue - cakephp

To explain the issue I'm having, I'll use an example. Let's say that I'm building a system where students can sign up for an afterschool class, but an administrator has to approve the sign-up in order for it to be valid. So I have these models:
Calendar (belongs to "Teacher", hasAndBelongsToMany "Student")
Student (hasAndBelongsToMany "Calendar")
Teacher (hasMany "Calendar")
Now let's say I want to see a list of all of the unapproved sign-ups that are for ninth-graders. I want the list to include the calendar date, the student's name, and the teacher's name. I would do something like this:
$this->request->data = $this->Student->CalendarsStudent->find('all', array(
'conditions' => array(
'CalendarsStudent.is_approved' => null,
'Student.grade' => 9
)
));
The problem with the above code is that the returned array is missing the teacher's name:
Array
(
[0] => Array
(
[CalendarsStudent] => Array
(
[id] => 1274
[calendar_id] => 200
[student_id] => 872
[is_approved] =>
)
[Calendar] => Array
(
[id] => 200
[date] => 2012-12-17
[teacher_id] => 1
[total_slots] => 15
)
[Student] => Array
(
[id] => 872
[teacher_id] => 1
[first_name] => Billy
[last_name] => Smith
[grade] => 9
)
)
)
If I add 'recursive' => 2 to the find parameters, I get way too much information. $this->request->data[0]['Calendar'] will have [Teacher], which is what I want, but it will also have [Student], which I don't want. Also, $this->request->data[0]['Student'] will have subarrays that I don't want. It seems like Containable would fix this, but I can't get that to work either.
Any ideas?

Use CakePHP's [Containable Behavior]. It's amazing - easy to use and will return whatever you want it to.
Side note: If you're using "recursive" as anything other than -1 for ANYTHING, it should be a red flag. Containable is the absolute way to go as it lets you specify easily and exactly what data you want returned. Recursive will often cause memory issues and or other badness.
Best practice IMO: set $recursive = -1; in your AppModel.php and never set it to anything else...ever.

Related

Cakephp HABTM same model

I'm trying to figure out how to track the owners of real property. Investors specifically.
(I did spend hours researching and trying different things)
I'm using HABTM on the same model. (see below for why)
The difficulty I'm having is on the model->find. It only returns related entries if the id I'm searching for is in the first column of the relationship table.
In the entities table I have
1,Homer
2,Springfield Nuclear
3,Bart
In entities_relateds
2,1
2,3
(for the sake of argument, let's just assume that Bart grew up and went to work at the plant.
So both 1 & 3 are related to 2.)
If if do
$this->Entity->findById(2);
It looks great.
Array
(
[0] => Array
(
[Related] => Array
(
[id] => 2
[name] => Springfield Nuclear
[0] => Array
(
[id] => 1
[name] => Bart
)
[1] => Array
(
[id] => 3
[name] => Homer
)
)
)
)
However, if the values in the first row of entities_relateds are reversed...
1,2
2,3
I get
Array
(
[0] => Array
(
[Related] => Array
(
[id] => 2
[name] => Springfield Nuclear
[0] => Array
(
[id] => 3
[name] => Homer
)
)
)
)
It doesn't return the first row.
Doesn't matter if I do
$this->Entity->Related->findById(2);
It changes the array a bit, but still doesn't return the first row.
I'm wide open to alternate suggestions for how to solve this.
Thanks.
A bit more information here below.
Scenario:
It's pretty common to have people partner on deals and to own the property in either their own name, or any of several different companies. Partner A, Partner B, Company A, Company B. Any of these names could be on title, and really these are just aliases for the same 'group'.
So, I created a table called 'entities'. Each record can be either a real person, or a company. They share a lot of the same attributes, but most importantly, either type can be the legal owner of a piece of real property.
CREATE TABLE `entities` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
The relation table:
CREATE TABLE `entities_relateds` (
`entity_id` INT(11) NOT NULL DEFAULT '0',
`related_id` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`entity_id`, `related_id`)
)
Model HATBM
App::uses('AppModel', 'Model');
class Entity extends AppModel {
public $hasAndBelongsToMany = array(
'Related' => array(
'className' => 'Entity',
'joinTable' => 'entities_relateds',
'foreignKey' => 'entity_id',
'associationForeignKey' => 'related_id',
'unique' => 'keepExisting'
)
);
}
I would recommend you to try setting the recursive of the model to 1 like:
$this->Entity->recursive = 1
and then try to do the find.
If this not works I'd give a shot to containable behavior which is even better than messing up with the recursivity of model.
If you choose to work with ContainableBehavior your find would end up like this:
$this->Entity->find('all',
array(
'conditions'=>array('id'=>2),
'contain'=>array('Related')
)
);
UPDATE 1: Added containable code.

Retrieve value from array in cakePHP

I want to know how to retrieve the values from array that is looking like that:
Array ( [0] => Array
( [Group] => Array
( [id] => 1
[name] => admin
[created] => 2013-04-15 14:13:19
[modified] => 2013-04-15 14:13:19
)
[Admin] => Array
( [0] => Array
(
[id] => 1
[email] => iman#yahoo.com
[username] => iman
[password] => 9e217e2039912c40b0f179f801e2d3e9fe8eb32e
[active] => 1
[mobile] => 01000000000
[created] => 2013-04-15 13:56:02
[modified] => 2013-04-15 14:44:59
[group_id] => 1
[tokenhash] => e2e1bbffc40d3f909594a268f0f3ec127fabe5c00e01c5f0644a1950aa37e6103ad18542a8731a2ad9ade283916281977677523098cd25a296116d078fbbc231
[image] => d
)...
Thanks.
What have you tried already?
As far as I can see based on the limited information provided, you have two options; access a value directly by providing the relevant keys or looping over the array.
Say you want to access the name of the first Group in the array, which I presume is stored in a variable (called $yourArray in this example):
$yourArray[0]['Group']['name']
The result will be 'admin'.
Looping would give you the benefit of retrieving all the group names (or any other value):
foreach ($yourArray as $value) {
//Output the Group name
echo $value['Group']['name'];
//Output the Admin email
echo $value['Admin'][0]['email'];
}
But the above is all fairly standard PHP stuff and not specific to CakePHP. It might be good to read up on the basics of PHP as well, as CakePHP adds another layer of abstraction by providing all kinds of framework conventions and convenience methods.

Deep association findAll() with CakePHP 2 models

I have models Trip, Investment, and Person. A single trip hasMany investments, and every investment belongsTo one Person. When I say $this->Trip->find('all'); I get:
Array (
[0] => Array (
[Trip] => Array (
[id] => 1
[date] => 2012-10-25 13:00:00
)
[Investments] => Array (
[0] => Array (
[id] => 1
[person_id] => 1
[investment_type_id] => 2
[trip_id] => 1
[investment] => 55
)
[1] => Array (
[id] => 2
[person_id] => 2
[investment_type_id] => 1
[trip_id] => 1
[investment] => 40
)
)
)
)
I would like each investment to include the information about the person represented by person_id (So their id, their name, etc). I've tried all levels of recursion on the find('all') lookup with no luck.
have you defined both ways of association in all models?
eg. Trip hasMany Investments AND Investment belongsTo Trip
Investment belongsTo Person AND Person hasMany Investments
also query should be
$this->Trip->find->("all", array("recursive" => 2));
other thing what could be problem is if all necessary models are loaded in your controller
See cakePHP model associations (retrieving data deeper) for an alternative solution:
Use
$this->YourModelname->recursive = 3;
before querying.

Using cakePHP's Hash class to extract data from an array

I have an array like this:
Array(
[Rating] => Array(
[0] => Array(
[id] => 4
[rating] => -1
),
[1] => Array(
[id] => 14
[rating] => 9.7
),
[2] => Array(
[id] => 26
[rating] => 9.55
)
)
)
I need to extract all the ratings >= 0 from this array, and JUST the numbers. I was doing this fine with Set::extract('/Rating/rating[rating>-1]', $video)
But I then learned that this is deprecated and you are supposed to use the Hash class now. So I looked up the new syntax and wrote:
Hash::extract($video, 'Rating.{n}.rating[rating>-1]');
which gives me the correct result, but it gives an annoying warning: Warning (4096): Argument 1 passed to Hash::_matches() must be an array, string given, called in E:\www\lib\Cake\Utility\Hash.php on line 131 and defined [CORE\Cake\Utility\Hash.php, line 170]
The warning doesn't occur when I don't have the condition in there (the [rating>-1]) but of course includes the -1's, which I don't want. What am I doing wrong here? Am I misusing this function somehow? Should I just use the deprecated Set class? Or should I just ignore the warning because it won't show up once I put this app into production mode?
This question is old and I'm sure it's already resolved, but the CakePHP documentation is a bit unclear regarding usage of the Hash path syntax. So, in case someone else has come across this, here's what I found.
The syntax in the question is not correct. The correct syntax is 'Hash::extract($ratings, 'Rating.{n}[rating>-1].rating')'.
According to the documentation: "Tokens are composed of two groups. Expressions, are used to traverse the array data, while matchers are used to qualify elements.".
{n}[rating>-1] is considered one token. {n} is the expression which filters the array keys, in this case the key must be numeric. [rating>-1] is the matcher which filters the array elements, in this case the element must be an array that contains a key named rating and an associated value that is greater than -1. Once you have the array element then you can get the rating.
$ratings = array(
'Rating' => array(
array(
'id' => 4,
'rating' => -1
),
array(
'id' => 14,
'rating' => 9.7
),
array(
'id' => 26,
'rating' => 9.55
)
)
);
print_r( Hash::extract($ratings, 'Rating.{n}[rating>-1].rating') );
Results in:
Array ( [0] => 9.7 [1] => 9.55 )

Covert into Cakephp query with subquery

Does anybody know how transform this query:
SELECT * from diminventory where partnumber='350964-B22' or partnumber in (SELECT partnumber from dimparts where parentpartnumber='350964-b22')
in a cakephp query
Thanks
I'm not 100% certain what you're asking for as yet, but here's a brief tutorial on cakephp querying.
$this->ModelName->query("SELECT * FROM tablename LIMIT 2;");
You query from the model, but you use the literal tablename. You use the SQL "AS" keyword to rename the keys of the resultant array.
Sample results:
Array
(
[0] => Array
(
[tablename] => Array
(
[id] => 1304
[user_id] => 759
)
)
[1] => Array
(
[tablename] => Array
(
[id] => 1305
[user_id] => 759
)
)
)
He is asking for Cakephp query not custom SQL query. In the controller:
$partnumber = $this->diminventory->find('all',array('conditions' => array('diminventory.partnumber' => '350964-B22')));
$this->set('partnumber',$partnumber);
set function passes a variable as $partnumber to the view. Then int view (which is .ctp file) you need to output the array.
foreach($partnumber as $partnumbers){
echo $partnumbers;
}
Make sure that diminventory table has 's' after the name otherwise it won't work as this is part of Cakephp's strict naming conventions.
Tutorial for find function:
http://book.cakephp.org/view/1018/find
To do it fully Cake'ish, you need to use CakePHP subquery:
$dbo = $this->User->getDataSource();
$subQuery = $dbo->buildStatement(
array(
'fields' => array('`Dimpart`.`partnumber`'),
'table' => $dbo->fullTableName($this->Dimpart),
'alias' => 'Dimpart',
'conditions' => array('`Dimpart`.`parentpartnumber`' => '350964-b22'),
),
$this->Dimpart
);
$subQuery = ' `DiminventoryEntry`.`partnumber` IN (' . $subQuery . ') ';
$subQueryExpression = $dbo->expression($subQuery);
$conditions[] = $subQueryExpression;
$conditions['DiminventoryEntry.partnumber'] = '350964-B22';
$result = $this->DiminventoryEntry->find('all', compact('conditions'));
Where Dimpart is your model for table dimparts, and DiminventoryEntry is your model for table diminventory (which is not actually Cake'ish, you should've renamed your table according to conventions).

Resources