CakePHP Recursive Find Method - cakephp

In my CakePHP app, I have the following model association:
Offer -> Order -> Coupon
Then, at OffersController.php, i need to write a find method to output
the number of coupons sold on each offer, assuming that some orders can have multiple coupons.
The problem is that I'm in the OffersController, so when I try to use find('count'), I just get the number of offer entries. I want to count the number of coupon entries.
I tried to do something like $this->Offer->Order->Coupon->find('count'), but it does not work.
I tried to use the ContainableHelper as well, but got into the same problem of counting the wrong entries.
How can I proceed?

You need the resulting SQL query to do some magic with COUNT and GROUP BY. Try to add a field like this (untested):
$this->Offer->Order->Coupon->find(
'all',
array(
// Generate a field that counts the coupons per Order
'fields' => array(
'COUNT(Coupon.id) AS coupon_count'
),
// Only look at Orders for the given Offer
'conditions' => array(
'Order.offer_id' => $id
),
// This is important: group by Order.
'group' => ('Coupon.order_id')
)
);
If your Order -> Coupon have a simple hasMany relationship, a more convenient approach would be to keep track of the existing Coupon count in every Order record using an extra coupon_count field and the counterCache function. CakePHP can automatically update that counter field for you.

Related

CakePHP: Limiting number in a group

I'm creating sort of a game, and I want people to be able build a town.
Each user can only build a limited number of a specific buildings in their town (i.e. only 2 post offices, 1 city hall etc.)
I'm struggling with how to implement this.
I have a UserBuildings table to keep track of the user/building associations, a Buildings table and a User table.
On the form where I add a building to my city, I want to first check the building's limit, and omit it from the list of available buildings if it's been reached. Here's what I have:
$buildings = $this->UserBuildings->Buildings->find('list');
$currents = $this->UserBuildings->find('all', array(
'conditions'=> array(
'UserBuildings.user_id'=>$this->Auth->user('id')
)
));
I can get the limits from the $currents array.
Is there a way, via find and query in which I can add some conditions to my $buildings list to remove any entries with a capacity reached.
Obviously, I can run a compare via a for loop and build a more refined list by checking capacities against number of matching entries in the UserBuildings table, but is there a more elegant method, perhaps using CakePHP's own methods?
Simply count how many buildings of a type an user has. By adding a new building you should already know it's id and other data.
Assuming this is ja join table:
$count = $this->UserBuildings->find('count', array(
'conditions'=> array(
'UserBuildings.building_id'=>$buildingId,
'UserBuildings.user_id'=>$this->Auth->user('id')
)
));
You can then simply compare the count with your limit for that building. all of that should happen in a model by the way.

CakePHP containable behaviour not firing

First time using Cake and its containable behaviour, but it's not working as expected ... or at all.
I'm trying to obtain a list of accessories for a product. Product model HABTM products (alias 'ProductRelation'). Join table is products_products which has two product ids - product_id and related_id. It's against this I want to pull the list of accessories (products driven from the related_id column) for a given product_id
In my Product model, I've added $actsAs = array('Containable');
And in my controller, a quick test of containable using reference from the cookbook fails to contain products at all, even without conditions.
debug($this->Product->find('all', array('contain' => 'ProductRelation')));
.. returns an array of every product in the db, with ALL related models - images, content tabs, ratings, reviews, pricing, etc I haven't tried applying any 'conditions' against this because the call as written should limit data to the product and it's ProductRelation data, according to the cookbook ...
Any tips?
It seems like you have recursive on. Try using the following:
debug($this->Product->find('all', array(
'contain' => 'ProductRelation',
'recursive' => -1
)));
If that works for you, you should start adding containable to the AppModel class and setting the recursive property to -1. This will ensure you only ever get the results you request.
NB: Cake does not join for HABTM, so you can not use ProductRelation in any conditions.

Possible to have association or not based on main model's data?

I have a nodes table (Node model). I'd like it to be associated to different data types, but only if one if it's field is set to 1.
Example:
My nodes table has a data_article field (tinyint 1). I only want the Node to $hasMany Article IF that field is a 1.
I tried this:
public $hasMany = array(
'Article' => array(
'conditions' => array('Node.data_articles' => '1')
),
);
But I get an error:
Column not found: 1054 Unknown column 'Node.data_articles' in 'where
clause'
Because the association is doing the Article find in it's own query:
SELECT `Article`.`id`, `Article`.`title`, `Article`.`node_id`, ...more fields...
FROM `mydatabase`.`articles` AS `Article`
WHERE `Node`.`data_artiles` = '1'
AND `Article`.`node_id` = ('501991c2-ae30-404a-ae03-2ca44314735d')
Obviously that doesn't work, since the Node table isn't being Joined at all in this query.
TLDR:
Is it possible to have associations or not based on a field in the main model? If not, how else can I keep different data types in multiple tables, and not have to query them all every time?
I don't think that there's a standard way to do this with CakePHP (at least I can't imagine a way). What definitely is possible would be binding associated models dynamically.
So you might query your model without associations by passing the recursive parameter as -1 to the find() method and based on the result unbind the associated models dynamically. Afterwards you would have to query again, for sure. You might build a behavior out of this to make it reusable.
A quite elegant solution would be possible, if Cake could make a two-step query with a callback after the main model was queried, but before associated models are queried, but this isn't possible at the moment.
Edit: There might be a non-Cake way to archieve this more performantly with a custom Query including IF-statements, but I'm not the SQL expert here.
You should do it from the other side:
public $belongsTo = array(
'Node' => array(
'conditions' => array('Node.data_articles' => '1')
),
);

cakephp habtm join issue

I have table structure like below:
events (boxing, sparring etc)
competitors (users who are participating into diff events)
events_competitors (every competitor's selected events will go here)
Now what i want is, as per my match schedules i want to find competitors from schedules tables, where each competitors will be matching with events they have selected at registration time.
I'm using a find query something like this:
$matchdivisions = $this->Competitor->find("all" , array(
'conditions' => array('Competitor.status' => 1,
'Competitor.payment_completed' => 1,
'Competitor.weightgroup_id' => $current_matchsc['Matchschedule']['weightgroup_id'],
'Competitor.rank_id' => $current_matchsc['Matchschedule']['rank_id'],
'Competitor.degree_id' => $current_matchsc['Matchschedule']['degree_id'],
'Competitor.gender' => $current_matchsc['Matchschedule']['gender'],
),
'joins' => array(
array('table' => 'event_competitors',
'alias' => 'EventCompetitor',
'type' => 'left',
'conditions'=> array('EventCompetitor.event_id = '.$current_matchsc['Event']['id']),
)
)
)
);
Here, I have used joins because I am not able to find relations from EventCompetitor matching with Competitors and Events.
Problem: The single matching record comes 10 times, because of join while it should be single time only.
Earliest reply would be appreciated.
Thanks in advance!
Now a next level once this worked for me:
I have two levels of conditions check, in EventsCompetitors I want to see, if they want to have a fight with black belt or if they are minor and they want to have fight with adults so in this case, I want to see these both flags and on that basis, again my conditions will be changed and a new results will be displayed for my additional displays.
Kindly let me know, if sometime anyone have idea on this kind of stuffs with CakePHP.
Again a lot thanks to stackoverflow for their excellent services !
If you are only interested in a single record and are always sure that the others are duplicates then you can use find('first', ...) instead.
If you are interested in multiple different records but you are getting multiples of each of those records you could add in a 'group' => array('') to aggregate the competitors on a specific field to only return them once ?

CakePHP: Unsure how to handle a semi difficult relationship

I'm working on my first CakePHP app, an order/invoice system. The order-part are coming along nicely, but for the invoices I kinda need some help.
The database structure I'm using; A delivery note consists of multiple products, which in turn exist of multiple re-usable elements (like a number of trays and the product itself). Now because some customer order larger quantities, they get lower rates, which are time-based (from week 1 to 9 €1 for element y, and from week 10 to 8 €1.20 for the same element). Of course some customers just have to use the daily prices, which will be stored the same way, just witha nulled customer_id.
Now my problem; I have absolutely no idea how I should tackle the invoice view, or more specifically; what the best way is to get the data, or if I should just go and practice my SQL-writing skills.
I'm not seeing any major problems with your schema. The Containable behavior should make things easy here. Add it to your Invoice model:
var $actsAs = array('Containable');
Make your life easier by adding a DefaultPrice relationship for each element. In your Element model:
var $hasOne = array(
'DefaultPrice' => array(
'className' => 'Price',
'foreignKey' => 'element_id',
'conditions' => array('DefaultPrice.customer_id IS NULL')
)
);
Now to build your invoice, set up and pass a contain parameter to your find operation. (I assume you want to show a breakdown of element costs, right?)
$contain = array(
'DeliveryNote' => array(
'Product' => array(
'Element' => array(
'DefaultPrice',
'Price' => array(
'conditions' => array('Price.customer_id' => $customer_id)
)
)
)
)
);
$this->Invoice->find('first', array('conditions' => ..., 'contain' => $contain));
This should result in each Element record including a DefaultPrice, and if the customer is receiving special pricing, the Price record will also be included.
Note: You may want to consider including a default_price field in the Element field, and avoid having to do the additional join above. Every Element is going to have a default price, right?

Resources