I am fairly new in cakePHP. I have a Blogs and Comments table having a one-to-many relationship. In the blogs controller, I have this code to get the blog data and the attached comments to it.
$blog = $this->Blogs->get($id, contain => ['Comments']);
$this->set(compact('blog'));
I am trying to populate the data of the Comments by using a foreach loop.
foreach ($blog->comments as $comment) :
echo $comment.'<br />';
endforeach;
However, the default order is ascending based on the id of the Comments table, and what I am trying to achieve is a DESC order based on Comments.created , so when I populate the data, the top most would be the latest created comment and so on.. Any kind of help is greatly appreciated!
You can provide extra information in contain. https://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#sorting-contained-associations
$blog = $this->Blogs->get($id, contain => ['Comments' => ['sort' => ['Comments.created' => 'DESC']]]);
$this->set(compact('blog'));
Related
Due to having to having to import data from an old non cake app and oddly built database table I need to pass paginate an array of records it is allowed to display - is this possible?
Normally I would reorganise the data into proper relationships etc but due to time scales etc this is not possible.
To give you more info - in my users table I have a field that contains a list of ID's that relate to documents they are allowed to view. The field will contain something like
123,23,45,56,765,122,11.9,71,25
Each ID refers to a document the documents model. I know that normally you would create proper ACOs and AROs and let the ACL/Auth componant handle which users can access what but this isnt an option this time around. So I thought if I could do it via paginate/find it might be an option?
Any help would be really appreciated.
Thanks in advance
You wouldn't pass it a list of IDs, you'd use the IDs in your paginate conditions - something like below.
(Code written off top of my head, so pardon any syntax errors...etc. It should give you the right idea/path at least):
//Controller
$this->loadModel('User');
$this->loadModel('Document');
$user = $this->User->findById($userId);
$documentIds = explode(',', $user['User']['doc_ids']);
$this->paginate = array(
'conditions' => array(
'id' => $documentIds
)
));
$documents = $this->paginate('Document');
When you pass an array as a condtion (eg. 'id'=>$arrayOfIds), it uses MySQLs "IN" - something like:
... WHERE id IN (45, 92, 173)
I've got my models set up so that Users belong to Groups. There is a working HABTM relationship between these two.
The code below is used to populate the database with some demo data. Prior to the following snippet, the Groups table has been filled with some sample group data.
The code snippet below works. It adds records to the Users table and the join table (groups_users) gets the correct entries, too. So basically, the HABTM relationship works fine.
Here is my question: How do I add associations with multiple groups instead of just the single relationship? Doing a 'Group'=>array($group1, $group2) instead of the 'Group'=>$group1' as outlined below does NOT work. When I use an array, nothing at all is added to the join table.
Your help is greatly appreciated!
$groups = $this->Group->find('all');
for ($i=0; $i<=200; $i++) {
$groupIndex1 = rand(0, count($groups)-1);
$groupIndex2 = rand(0, count($groups)-1);
$this->User->set(
array(
'id'=>null,
'name'=>'Dummy User '.$i0,
'Group'=>$groups[$groupIndex1]
)
);
$this->User->save();
To save multiple groups for a single user, you will have to define hasMany relationship in your User model. And in your saving code it will looks like:
$groups = $this->Group->find('all');
for ($i=0; $i<=200; $i++) {
$groupIndex1 = rand(0, count($groups)-1);
$groupIndex2 = rand(0, count($groups)-1);
$data =
array('User' => array(
'id'=>null,
'name'=>'Dummy User '.$i0,
),
'Group'=> array($groups[$groupIndex1], $groups[$groupIndex2])
)
);
$this->User->saveAssociated($data, array('deep' => true);
This link might help you to understand 'deep' => true option.
I'm new to learning CakePHP. I did the Blog Tutorial and am now trying to add Categories for Posts. I created Category and SubCategory Models and MySQL DB Tables and I related the Models as follows:
Post -> "belongsTo" -> SubCategory -> "belongsTo" -> Category
Post -> Subcategory is working fine and I can resolve the SubCategory Name in the View via:
php echo $post['SubCategory']['name'];
Now: How do I go one step further in the relation and get the Category Name for a Post in the Post View (via the SubCategory)? The following obviously gives me the Category ID, but not it's name:
php echo $post['SubCategory']['category_id'];
Thanks a lot!
Check out the recursive parameter for a model and go for a step 2 (2 levels of depth). This would allow you to use the category if your definition is correct. Keep in mind that it would need to fetch a lot of data and this would affect the overall performance of the site.
You should look into ContainableBehavior, which will help you only grab the results you really need. The first thing I always suggest is changing $recursive = -1 and using Containable. This will also greatly improve the performance of your app because you will be performing less calls for data you don't actually use.
Using your example:
$results = $this->Post->find('all', array(
'contain' => array(
'SubCategory' => array(
'Category'
)
)
));
// in your view foreach loop
echo $post['SubCategory']['Category']['name'];
New to CakePHP, so be gentle...
I'm trying to loop through a table to display all the read-only fields (labels and values).
In my edit function, I get the table
$this->set('invoice', $this->Invoice->read(null, $id));
In my view, I want to loop through the entire table schema and output the field names and values as labels like fieldName: value
Invoice Number: SVC00158
Invoice Date: 03/03/12
There are 37 fields in this table. I would rather not have to manually code for every one. I know to retrieve the input fields like this
echo $this->Form->input('purchaseOrderNumber');
but I can't seem to find a 'read-only' attribute for the input() method. Hope that makes sense.
Thanks for your time.
You could loop through the schema and output it like that:
// assumes $result contains model data
$schema = $this->Model->schema();
foreach ($schema as $field => $attrs) {
echo $result['Model'][$field];
}
just write:
<?=$this->Form->inputs();?>
If you are a beginner I suggest you look into Cake Bake (it will "bake" all your files from the console).
I mean the views, controllers, and models based on you current db schema so that will it for you and even apply some styling to it.
I hope that helps
I have a n...n structure for two tables, makes and models. So far no problem.
In a third table (products) like:
id
make_id
model_id
...
My problem is creating a view for products of one specifi make inside my ProductsController containing just that's make models:
I thought this could work:
var $uses = array('Make', 'Model');
$this->Make->id = 5; // My Make
$this->Make->find(); // Returns only the make I want with it's Models (HABTM)
$this->Model->find('list'); // Returns ALL models
$this->Make->Model->find('list'); // Returns ALL models
So, If I want to use the list to pass to my view to create radio buttons I will have to do a foreach() in my make array to find all models titles and create a new array and send to the view via $this->set().
$makeArray = $this->Make->find();
foreach ($makeArray['Model'] as $model) {
$modelList[] = $model['title'];
}
$this->set('models', $models)
Is there any easier way to get that list without stressing the make Array. It will be a commom task to develops such scenarios in my application(s).
Thanks in advance for any hint!
Here's my hint: Try getting your query written in regular SQL before trying to reconstruct using the Cake library. In essence you're doing a lot of extra work that the DB can do for you.
Your approach (just for show - not good SQL):
SELECT * FROM makes, models, products WHERE make_id = 5
You're not taking into consideration the relationships (unless Cake auto-magically understands the relationships of the tables)
You're probably looking for something that joins these things together:
SELECT models.title FROM models
INNER JOIN products
ON products.model_id = models.model_id
AND products.make_id = 5
Hopefully this is a nudge in the right direction?
Judging from your comment, what you're asking for is how to get results from a certain model, where the condition is in a HABTM related model. I.e. something you'd usually do with a JOIN statement in raw SQL.
Currently that's one of the few weak points of Cake. There are different strategies to deal with that.
Have the related model B return all ids of possible candidates for Model A, then do a second query on Model A. I.e.:
$this->ModelB->find('first', array('conditions' => array('field' => $condition)));
array(
['ModelB'] => array( ... ),
['ModelA'] => array(
[0] => array(
'id' => 1
)
)
Now you have an array of all ids of ModelA that belong to ModelB that matches your conditions, which you can easily extract using Set::extract(). Basically the equivalent of SELECT model_a.id FROM model_b JOIN model_a WHERE model_b.field = xxx. Next you look for ModelA:
$this->ModelA->find('all', array('conditions' => array('id' => $model_a_ids)));
That will produce SELECT model_a.* FROM model_a WHERE id IN (1, 2, 3), which is a roundabout way of doing the JOIN statement. If you need conditions on more than one related model, repeat until you have all the ids for ModelA, SQL will use the intersection of all ids (WHERE id IN (1, 2, 3) AND id IN (3, 4, 5)).
If you only need one condition on ModelB but want to retrieve ModelA, just search for ModelB. Cake will automatically retrieve related ModelAs for you (see above). You might need to Set::extract() them again, but that might already be sufficient.
You can use the above method and combine it with the Containable behaviour to get more control over the results.
If all else fails or the above methods simply produce too much overhead, you can still write your own raw SQL with $this->Model->query(). If you stick to the Cake SQL standards (naming tables correctly with FROM model_as AS ModelA) Cake will still post-process your results correctly.
Hope this sends you in the right direction.
All your different Make->find() and Model->find() calls are completely independent of each other. Even Make->Model->find() is the same as Model->find(), Cake does not in any way remember or take into account what you have already found in other models. What you're looking for is something like:
$this->Product->find('all', array('conditions' => array('make_id' => 5)));
Check out the Set::extract() method for getting a list of model titles from the results of $this->Make->find()
The solution can be achieved with the use of the with operation in habtm array on the model.
Using with you can define the "middle" table like:
$habtm = " ...
'with' => 'MakeModel',
... ";
And internally, in the Model or Controller, you can issue conditions to the find method.
See: http://www.cricava.com/blogs/index.php?blog=6&title=modelizing_habtm_join_tables_in_cakephp_&more=1&c=1&tb=1&pb=1