Find with Sum and Group By - cakephp

My MODELS ARE
1 Project hasMany Records
Records belogsTo Project
Table project (id, pname)
Table records(id, projects, tempo)
I want to get a SUM of time spent per project.
In My RecordsController i do:
$total = $this->Record->Project->find('list', array('fields' =>
array('SUM(Record.tempo) as tempoGasto','Project.pname'),
'group'=>array('Project.pname')
));
But this gives me this error:
missing entry for table "Record"`
What can I do to fix it?

$total = $this->Registo->Project->find('list', array(
'fields' => array(
'SUM(Registo.tempo) as tempoGasto',
'Project.pname'
),
'group'=>array('Project.pname'),
'joins' => array(
array(
'alias' => 'Registo',
'table' => 'records',
'type' => 'LEFT',
'conditions' => array(
'....'
)
)
));
I can't figure out your foreign key so, fill it up. This should work fine

Related

CakePHP only get results that have a related model

In my CakePHP 2 project, I have Projects that have many Articles, an Article can belong to many projects (a many-to-many relation).
Now I would like to find all Projects that have an Article.
My current code for getting the Projects is as follows
$projects = $this->Project->find('list', array(
'fields' => array('Project.slug', 'Project.name')
));
I tried adding contain to the query, without results
$projects = $this->Project->find('list', array(
'contain' => array('PressArticles' => array()),
'fields' => array('Project.slug', 'Project.name')
));
How can I modify this so I receive all projects that have an article?
Containing won't help, non-1:1 relations will be retrieved in a separate query, so that won't have any effect on your main query.
You could for example manually create INNER joins with the association's join and target table, that would automatically filter out all projects that have no associated articles, something along the lines of this (I've more or less guessed the names, it's just a quick and dirty example):
$projects = $this->Project->find('list', array(
'fields' => array('Project.slug', 'Project.name'),
'joins' => array(
array(
'table' => 'press_articles_projects',
'alias' => 'PressArticleProject',
'type' => 'INNER',
'conditions' => array(
'PressArticleProject.project_id = Project.id',
),
),
array(
'table' => 'press_articles',
'alias' => 'PressArticle',
'type' => 'INNER',
'conditions' => array(
'PressArticle.id = PressArticleProject.press_article_id',
),
)
),
'group' => 'Project.id'
));
Or if you are using a counter cache, then filtering by the counter would be an option too:
$projects = $this->Project->find('list', array(
'fields' => array('Project.slug', 'Project.name'),
'conditions' => array(
'Project.press_article_count >' => 0
)
));
See also
Cookbook > Models > Associations: Linking Models Together > Joining tables
Cookbook > Models > Associations: Linking Models Together > counterCache - Cache your count()
In your Project model, you can do
public $hasMany = array(
'PressArticle' => array(
'className' => 'PressArticle',
'foreignKey' => 'project_id'
)
);
For more info, check here

JOIN in CakePHP - A better understanding

I'd like to understand joins in CakePHP (v 2.4.5) a bit better by solving the following example:
Post hasMany Comment
Post.id == Comment.post_id
Comment.published can be 1 or 0
I need to find all Posts that have at least one published Comment
I want to write the query from the Post model. In order not to break pagination and so I can add order/conditions based on Post
I do not want to filter out results afterwards in PHP (in order not to break pagination)
You might suggest to approach this issue from the Comment model like here:
https://stackoverflow.com/a/3890461/155638
But this is about understanding joins better, so I'd like to set a requirement to write the query from the Post model.
I have roughly the following idea, hoping that the RIGHT join would exclude all non conforming Posts:
$this->Post->find('all', array(
'joins' => array(
array(
'table' => 'comments',
'alias' => 'CommentsJoined',
'type' => 'RIGHT',
'conditions' => array(
'Post.id = CommentsJoined.post_id',
'CommentsJoined.published = true'
)
)
),
'contain' => array(
'Comment' => array(
'conditions' => array(
'Comment.published' => 1
)
)
)
);
But it did not work for me yet.
Currently my query returns 19 times the same Post, instead of 19 unique Posts.
How to go from here? Is the approach the right one?
Kind regards!
Bart
It seems I was on the right track. The final step was to remove the duplicate Posts.
This is done by adding 'group' => 'Post.id' as an attribute to the query.
Like this:
$this->Post->find('all', array(
'joins' => array(
array(
'table' => 'comments',
'alias' => 'CommentsJoined',
'type' => 'RIGHT',
'conditions' => array(
'Post.id = CommentsJoined.post_id',
'CommentsJoined.published = true'
)
)
),
'group' => 'Post.id',
'contain' => array(
'Comment' => array(
'conditions' => array(
'Comment.published' => 1
)
)
)
);

Select records which have atleast one hasMany relation row in Cakephp

I'm facing a problem with cakephp associations in Models.
I have to Select records which have atleast one hasMany reation row
Model
class Category extends AppModel
{
public $hasMany = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'CategoryId',
)
);
}
Query
$categories = $this->Category->find('all');
I only needed the categories which have atleast one product entry
Categories Like : Shirts, Footwear, Glasses etc
Products like :
Small, medium, large (Shirts)
With Frame, UV protected (Glass)
So, i jus want to get Shirts and Glasses Categories only because for the above example there is no products for Footwear
Use counterCache or joins
Please refer to CakePHP - Find and count how many associated records exist
The most simple way with the best performance would be using a properly indexed counter cache field as shown in the linked answer.
Sice the linked answer is not an exact duplicate with respect to the join, here's some additional info, instead of using HAVING COUNT with the join you'd use a IS NOT NULL condition. Here's an (untested) example:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'conditions' => array(
'Product.CategoryId IS NOT NULL'
)
'group' => 'Category.id'
));
Depending on the used DBMS and version you might get better performance using an inner join:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'group' => 'Category.id'
));

How to find by conditions in two joining tables in CakePHP

In my CakePHP app I have three tables:
Businesses, Towns and Categories.
A business can belong to multiple towns and multiple categories so I have created joining tables and hasMany and belongsTo relationships. Everything works fine when finding businesses by either Town or Category by using the Town or Category model to search, but I am completely stuck when I want to search for businesses in a certain town AND a certain category, eg. Plumbers in London.
The associations just don't seem to work when searching with the Business model and I get column not found errors when trying to use the associated tables. I would think that this would be along the lines of what needs to be done, but I can't get it to work:
$this->set('listings', $this->Business->find('all', array(
'conditions' => array(
'Business.approved' => 1,
'BusinessesCategory.category_id' => $id,
'BusinessesTown.town_id' => $town_id,
'Business.sasite' => 1
)
You need to join the tables to do that.
I will put above a example how has to work with category and you can do the town yourself.
$this->Business->find("all", array(
"joins" => array(
array(
"table" => "businness_categories",
"alias" => "BusinessesCategory",
"type" => "LEFT",
"conditions" => array(
"Businesses.id = BusinessesCategory.business_id"
)
),
array(
"table" => "categories",
"alias" => "Category",
"type" => "LEFT",
"conditions" => array(
"BusinessesCategory.category_id = Category.id"
)
)
),
'conditions' => array(
'Business.approved' => 1,
'Category.id' => $id,
)
));
You also could use a behavior to do that for you:
https://github.com/Scoup/SuperJoin
Hi I had a very similar setup and the same problem. This is how I would solve your problem:
As you dont give away to much of your code I make some assumptions:
- You implemented your search method in the BusinessController
- Your search argument for the town is stored in vaiable $where and the one for Category is stored in $what
Code if you only have conditions for one table
$this->Businesses->Town->recursive = -1;
....
$options['joins'] = array(
array('table' => 'towns',
'alias' => 'Town',
'type' => 'inner',
'conditions' => array(
'Business.town_id = Town.id',
)
)
);
$options['conditions'] = array(
'Town.townName' => $where
);
$result = $this->Business->find('all', $options);
Code if you have conditions for two table
$this->Businesses->Town->recursive = -1;
$this->Businesses->Category->recursive = -1;
....
$options['joins'] = array(
array('table' => 'towns',
'alias' => 'Town',
'type' => 'inner',
'conditions' => array(
'Business.town_id = Town.id',
)
),
array('table' => 'categories',
'alias' => 'Category',
'type' => 'inner',
'conditions' => array(
'Business.category_id = category.id',
)
)
);
$options['conditions'] = array(
'Town.townName' => $where,
'Category.categoryName' => $what
);
$result = $this->Business->find('all', $options);
You can use
$this->Business->find('all', array(
'conditions' => array(
'AND' => array(
'BusinessesTown.town_id' => $town_id,
'BusinessesCategory.category_id' => $id
)
),
'recursive' => 2
));

cakePHP table joining two tables issue

Im new to cakePHP and the whole table relations concept is very confusing!
I have 2 tables, competencies and competenceRatings. competencies stores a list of names with ids.
competencies
------------
id
name
And users can select various competencies from this table and rate them and their ratings are stored into competenceRatings table.
competenceRatings
-----------------
id
competence_id
user_id
rating
I want to be able to get the names of competencies for which a user have NOT made any ratings into competenceRatings table. i.e., I need list of names from competencies table for which there are no entries in comptenceRatings table(for given user_id).
I tried competencies->hasMany->competenceRatings, competenceRatings->belongsTo->competencies relations.
$competencies = $this->Competence->CompetenceRating->find('all',array('CompetenceRating.user_id' => $userId,'CompetenceRating.competence_id !=' => 'Competence.id'));
But no use!
Does this result require any other relations? Or can i just join tables using joins condition in find query?
Thanks.
EDIT
This method worked:
$options['joins'] = array(
array(
'table' => 'competence_ratings',
'alias' => 'CompetenceRating',
'type' => 'LEFT OUTER',
'conditions' => array(
'Competence.id = CompetenceRating.competence_id',
'CompetenceRating.user_id' => $userId
)
)
);
$options['conditions'] = array( 'CompetenceRating.competence_id'=> null );
Try this
Joining tables
$data = $this->Competence->find('all', array('joins' => array(
array(
'table' => 'competenceRatings',
'alias' => 'CompetenceRating',
'type' => 'inner',
'foreignKey' => false,
'conditions'=> array('CompetenceRating.competencie_id = Competence.id')
),
array(
'table' => 'competencies',
'alias' => 'Competence',
'type' => 'inner',
'foreignKey' => false,
'conditions'=> array(
'Competence.id = MarkersTag.tag_id',
'Competence.user_id' => $user_id
)
)
)));

Resources