I have a simple cakephp app with table articles that has a cat_id column to reference a id column of categories in a cats table.
What I would like to do is display all categories names, each followed with a count of the number of articles belonging to that category.
I know there is a find('count') function in cakephp, but that only working on one table/field. How do I link the two tables to get a list of category names and count of articles for each category for my view?
Thanks
COme on! Use counterCache!
In table "cats" create field article_count. In Model write
/app/models/cat.php
class Cat extends AppModel
{
var $name = 'Cat';
var $belongsTo = array('Article'=>array('counterCache'=>true));
}
That's all! every time you add/remove from articles, it writes to cats table count of articles.
don't forget to include article_count into fields list
it might be better to do this in the afterFind callback on the model but can be done in the controller aswell.
$cats - $this->Cat->find('all',array('recursive'=>-1));
foreach($cats as $key=>$cat){
$cats[$key]['ArticleCount'] = $this->Cat->Article->find('count',array(
'conditions'=>array('Article.cat_id'=>$cat['Cat']['id']))
);
}
or see if this works
$cats = $this->Cat->find('all',array('contain'=>array(
'Article'=>array('fields'=>array('COUNT(*) AS ArticleCount')
)));
obviously you will need to add the containable behavior on the category model.
You could also use the countercache feature: http://book.cakephp.org/view/816/counterCache-Cache-your-count
You also might consider checking out counterCache to cache the count within a column value.
Thanks, this worked:
$cats = $this->Article->Cat->find('all',array('recursive'=>-1));
foreach($cats as $key=>$cat){
$cats[$key]['ArticleCount'] = $this->Article->find('count',array(
'conditions'=>array('Article.cat_id'=>$cat['Cat']['id']))
);
}
Related
I'm just started to work with Laravel and think its a pretty good framework.
But there is a lot to learn and i can mostly find everything in the user guide except this part:
I'm trying to get items from my database they are sorted with a category id that relates to a other table item_catagories in this table are stored:
id
name
parent
In my url of the website I use the name of the category instead of the id.
http://example.com/catagory/subcatagory
when subcatagory has a value I want to search for the related items.
I now have it like this:
if($subcategory){
$foo = ItemCategories::where(['group' => $category, 'name'=> $subcategory])
->get()[0]->id;
$data['products'] = Items::where('category_id', $foo)->get();
}
but there must be a much simpler way to get the same results.
I hope someone can help me to understand how I can do it better
Edit
I forgot to add the relation code:
The item class:
public function categorie(){
return $this->hasOne('App\ItemCategories');
}
The categorie class:
public function items(){
return $this->belongsTo('App\Items');
}
You can use Laravel Eloquent's relationships for this. On your category model:
public function items() { return $this->hasMany('Items'); }
Once you've done that, on a category, you can do $category->items to fetch all of its related items.
Incidentally, when you do this:
->get()[0]
you can just do this:
->first()
If you wish to bring your results already populated, one way to do this is:
$foo = ItemCategories::where(['group' => $category, 'name'=> $subcategory])->with('items')->first();
So far I have this code:
$list = $this->modelsManager
->createBuilder()
->columns(array("SeriesList.*", "User.*", "Entry.*"))
->from("SeriesList")
->join("User")
->join("Entry")
->andWhere("SeriesList.id = '$id'")
->getQuery()
->execute();
Each row in the "Entry" database is an item that has a id, id of the series list it belongs to, its series id, and what type of series it is. The type of series specifies what table the series id can be found in.
The problem is that I want SeriesList(s) to may contain different types of objects. For example, the SeriesList "Favorites" may contain 3 shows and 4 books, Show and Book being different models which interface with separate tables.
Would I have to keep joining for all the models the list might contain? Or is there a better way?
The query builder will return a resultset which could be a model itself or a combined model (if you are doing joins etc.). It will not return objects of other models.
What you can do is use the getRelated() function to access the relationships from the base model itself.
So if your starting point is Entry then you could do something like this:
$entries = Entries::find();
foreach ($entries as $entry) {
// If series type is X
if ($entry->seriesType == 'x') {
$newItem = $entry->getRelated('x');
} else if ($entry->seriesType == 'y') {
$newItem = $entry->getRelated('y');
}
}
You can define relationships in your models that will cater for that. The key here is that Phalcon's relationships will relate data from table A to table B straight up and without any "filtering". So if you want to relate Entries to Favorites if SeriesList == 'favorite' you cannot, at least automatically. You will have to do that with code similar to the one above.
lets say I have three Models: Chapter, Book, Author.
A Book hasMany Chapter && Chapter belongsTo Book.
Author hasMany Book && Book belognsTo Author.
That means, that the ChapterTable has a book_id and the BookTable has an author_id.
Now I want to count all the chapters, from a specific author. How would I go about it using Blade?
Here is what I am thinking about:
$chapters = Book::where('author_id', $author)->with('chapters')->get();
$chapters = lists('chapter_title');
But those lines are not working properly, because the chapters are saved as an array inside the book_array and I am not able to access the chapters directly as suggested in "lists('chapter_title');"
Of course a solution would be, to give every chapter Table an author_id, and then I just could do:
$chapters = Chapter::where('author_id', $author)->get();
$chapters = lists('chapter_title');
I know this would work, but isn't there the possibility to get above results, without having an authors id on my chapters table?
Regards,
George
Eloquent way using hasManyThrough:
// Author model
public function chapters()
{
return $this->hasManyThrough('Chapter', 'Book');
}
// then
$author->chapters()->count(); // single query for a single author
In order to eager load the count for multiple authors you need for example this:
// Author model
public function chaptersCount()
{
return $this->chapters()->selectRaw('count(*) as aggregate')->groupBy('author_id');
}
public function getChaptersCountAttribute()
{
if ( ! array_key_exists('chaptersCount', $this->relations)) $this->load('chaptersCount');
return $this->getRelation('chaptersCount')->first()->aggregate;
}
Then:
$authors = Author::with('chaptersCount')->get();
$authors->first()->chaptersCount;
// or for a single author
$author->chaptersCount; // load the relation if needed
you might want to look at this http://laravel.com/docs/eloquent#querying-relations
$chapters = Book::has('author', $author)->with('chapters')->get();
$chapters = lists('chapter_title');
and for your chapters you can access them like Book->chapters
I have these tables in DB:
[posts, cats (categories), posts_cats (pivote)]
the relation between posts table and cats is many to many
I declared the relation in the models classes:
//Post.php
public function cats()
{
return $this->belongsToMany('cats');
}
//Cats.php
public function post()
{
return $this->belongsToMany('posts');
}
the question is, How to insert new post with multiple categories?
thanks,
Let's say you know the id of the post then you can attach a single cat like this:
Post::find($post_id)->cats()->attach($cat_id);
Or attach multiple cats like this:
$cat_ids = array(1,2,3,4);
Post::find($post_id)->cats()->attach($cat_ids);
If you got the Post model object in a variable, lets say $post:
$post->cats()->attach($cat_id);
// Or with multiple
$cat_ids = array(1,2,3,4);
$post->cats()->attach($cat_ids);
If you have a single category as an model object in, lets say $model:
$post->cats()->save($model);
Watch out with #Gadoma's answer. Its not wrong, but if you want to add categories to an post that already has categories then you should use attach() instead of sync(). Sync() will delete all others that are not provided to it when used.
edit:
So if you are creating a new Post then you probably are doing something like this:
$post = new Post;
$post->title = 'The title';
$post->something_else = 'Lorem';
$post->save();
//So now you have both the model object (the $post variable) and the id ($post->id).
$post->cats()->attach($cat_ids);
When you insert the post, then itterate over the categories and attach them to the new post. Something like that:
// $categories is an array of the categories to attach
foreach ($category_id in $categories) {
// Get a category object
$category = CategoryModel::find($category_id);
// $post is the new post
$post->cats()->attach($category);
}
I hope it helps you.
From the docs http://laravel.com/docs/eloquent#inserting-related-models
Inserting Related Models (Many To Many)
[...]
You may also use the sync method to attach related models. The sync
method accepts an array of IDs to place on the pivot table. After this
operation is complete, only the IDs in the array will be on the
intermediate table for the model:
And a code example:
$post = new Post(array('field1'=>'value1','fieldN'=>'valueN')) //example create new post
$categoryIds = array(1,3,4,5); //ids of (cats) categories you want the post to go into
$post->cats()->sync($categoryIds); //synchronise pivot table content with $categoryIds
I'm trying to sort records per categories and each record has one or more categories if the user has categories he likes.. If none, then just display all records..
Example, if a record has Food Category and the user signed in happens to have selected Food as one of his interests then the list of records displayed will be sorted having the records under Food category on top of the list..
I have
$this->Steplist->virtualFields['in_like'] = "IF(Record.category_id IN ($interests), 0, 1)";
$order["in_like"] = 'asc';
then
'order' => $order
for $this->Paginator->settings
then
int for the category_id
Now, instead of having 1 category for each record, it has to be changed to several categories. To avoid changing so much since the site is complete now, I simply change the category_id (int) to categories (varchar) with category ids separated by commas.. so instead of 5 for example, it can now be 3,5,7 if there are several categories attached to the record..
I tried to change the virtual field condition to:
$this->Steplist->virtualFields['in_like'] = "IF(Record.categories IN ($interests), 0, 1)";
but the result is not correct.
How do I do this instead (cakephp 2.4.3)?
This will work on settigns pagination.
class RecipesController extends AppController {
public $components = array('Paginator');
public function list_recipes() {
$paginate = array(
'limit' => 25,
'order' => array(
'Post.title' => 'asc'
)
);
$this->Paginator->settings = $paginate;
// similar to findAll(), but fetches paged results
$data = $this->Paginator->paginate('Recipe');
$this->set('data', $data);
}
}
http://book.cakephp.org/2.0/en/core-libraries/components/pagination.html
Limitations of virtualFields
But in general if you are using virtual fields: The implementation of virtualFields has a few limitations. First you cannot use virtualFields on associated models for conditions, order, or fields arrays. Doing so will generally result in an SQL error as the fields are not replaced by the ORM. This is because it difficult to estimate the depth at which an associated model might be found.
You can use asort() or sort() after retrieving your data