Laravel mySQL view return by specific ID - php

So I am quite new to Laravel, and I have a situation, where I am trying to gather data from a pivot table (contains 2 foreign keys only) in order to retrieve data from other tables.
Before everything, I'd like to note, that word "campaign" is the same as "box". Simply it differs in database and front.
I have multiple boxes, that contains specific gifts.
I have set the URL of the box to be something as such: http://127.0.0.1:8000/box/1
http://127.0.0.1:8000/box/2
etc..
I have done so, by simply using a button with the {id}:
View the box
My plan is, to print out only that specific boxes gifts (right now, all boxes print out all gifts).
I have tried to use the ->where option within my function, although, it seems that I can't try equaling to the campaigns ID.
Incorrect code:
function box(){
$data = array(
'list'=>DB::table('campaigns_gifts')
->join('gift_items', 'gift_items.id', '=', 'campaigns_gifts.gift_foreignK')
->select('gift_items.*')
->where($campaign_foreignK = '{id}')
->get()
);
return view('DBqueries.boxView', $data);
}
My question is, how can I specifically return data, that only belongs to that specific box, since I am not able to use mysql where option.
For reference, these are the database tables:
Basically, I would need to match my URL's id with campaign_foreignK
Thank you in advance.

First of all, yout need to start to use Laravel Eloquent Models.
But doing by your way (the hardest):
You need to create a route in web or api, something like that:
Route::get('/box/{id}', [BoxController::class, 'view']);
Then you need to put this function on your controller:
function view($id){
/**
* You can do it by 2 ways:
* 1 - Do a where in the result of DB query (the bad way)
*/
$list = DB::table('campaigns_gifts')
->join('gift_items', 'gift_items.id', '=', 'campaigns_gifts.gift_foreignK')
->select('gift_items.*')
->where($campaign_foreignK = '{id}')
->get();
$list = (array)collect($list)->where('abc', 123);
/**
* Or the second way (the best is to use the Eloquent, but using DB the following is the best)
* 1 - Get the relations:
* Is git_items id the key for gift_foreignK ? i'm supposing that is it! so....
*/
$giftsIds = array_values((array)DB::select("select * from campaigns_gifts where campaign_foreignK = $id"));
$giftsIdsString = implode($giftsIds, ',');
$list = (array)DB::select("select * from gift_items where id in ($giftsIdsString)");
return view('DBqueries.boxView', ['list' => $list]);
}

Related

Laravel - How to query if an array field in DB contains a value

I have a field in my model Person called jobs that I'm casting as an array. jobs is an array of ids related to a Jobs table. I want to be able to query Person and return all that have a certain id in their jobs array. I see that Laravel has a whereIn clause which checks if a database value is in an array but I need the opposite - to check whether a database array contains a value.
Am I stuck having to use where('jobs', 'like', '%"' . $job_id . '"%')?
I'm not sure there's an opposite however if you're simply looking to make the query a bit more reusable, you could make it a local scope by adding this to your Person model:
/**
* Scope a query to only include persons with given job id.
*
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeHasJob($query, $jobId)
{
return $query->where('jobs', 'like', "%\"{$jobId}\"%");
}
The name of the scope hasJob may interfere with the parent QueryBuilder method has so you might have to come up with a different name for it.
Now you can use Person::hasJob($job->id). However rather than storing the job ids in a column as an array, you should consider creating a pivot table to map the relationships between a person and job. You can do this using php artisan:
php artisan generate:pivot persons jobs
php artisan migrate
Then you need to add the relationship into your Person model:
/**
* The person's jobs
*/
public function jobs()
{
return $this->belongsToMany('App\Job');
}
So you can query your Person model by Job like this:
Person::whereHas('jobs', function ($query) {
return $query->whereId($job->id);
});
Laravel includes whereJsonContains(): So your field jobs that you
are casting as an array, that can query as :
->whereJsonContains('jobs', 3)
That way worked for me ...
I am adding a little more info to Zubayer Hossain's answer
The data types have to match:
// [1, 2]
->whereJsonContains('players', 1) // Works.
->whereJsonContains('players', '1') // Doesn't work.
// ["1", "2"]
->whereJsonContains('players', '1') // Works.
->whereJsonContains('players', 1) // Doesn't work.
whereJsonContains can be used in cases where we need to check if a value matches a json encoded field in our table.
Courtesy : https://newbedev.com/php-wherejsoncontains-and-with-laravel-example
You could use something like this query.
$k = ["359045532","359079612","359079372","359081292","359081052","359086332","359086092","359111892","359111652"];
Modal::whereIn('myitems', $k)->get();
<!-- If you have a collection of value like this: -->
$category_id = 1,2,3,...;
$category_id = $_POST['category_id'];
$myArray = explode(',', $category_id);
<!-- If you already have array data you can pass this to the following query -->
$data = DB::table('tablename')->select('*') ->whereIn('catcode', $myArray)->get();

How to match data from two different database tables and display it

I am looking for a solution which would withdraw essential data used to match other data from a different table. Then I would like to display this data inside a blade in a form of a table.
Inside the database, I have a "matching" entity which stores user's credentials which I would like to use for matching (for example desired price of the product). Entity contains "peopleID" as "matching" belongs to website users. When user is created, we assign matching options which are getting stored inside that "matching" entity. The number of rows inside Matching entity depends on the number of counties chosen during user creation stage.
I know that to withdraw matching data from the database I need to use a foreach loop.
The problem I have is when I output data inside the blade. For some reason it matches products only with the last item from an array. It should match prodtucts with all matching credentials.
Code:
$matchings = Match::where('PeopleID', '=', $id)->get();
$sales = DB::table('sales');
foreach ($matchings as $matching)
{
$county = $matching->county;
$sales->where('county', '=', $county);
}
$results = $sales->get();
So for one of the customers I have two matchings with different "counties". It only displays data for the last one added. How could I make it display data for other matching which contains a different county. I hope you know what I mean.
Thanks for any help.
Update - Major part of the code is done. Thank you for your help.
The second question is about adding the rest of matching options. As stated before the number of matches depends on the number of counties added. Each match has its own attributes. The idea is to show matched results for each county.
I know I will need some if statements to do this.
Here is an example which I would like to implement:
$maxprice = $match->maxprice;
$minprice = $match->minprice;
$brand_a = $match->brand_a;
$brand_b = $match->brand_b;
if($maxprice != '0')
{
$sales = DB::table('sales')->where('maxprice', '<=', $maxprice);
}
if($minprice != '0')
{
$sales = DB::table('sales')->where('minprice', '>=', $minprice);
}
if($brand_a == '1')
{
$sales = DB::table('sales')->where('brand_a', '1');
}
if($brand_b == '1')
{
$sales = DB::table('sales')->where('brand_b', '1');
}
To this code:
$user = User::find($id); // get our User with the Id (person id?)
$matches = $user->matches; // get all the matches
// or you could one line the above: $matches = User::find($id)->matches;
// get all the counties in the returned matches, could use pluck() method in higher version of laravel
$counties = [];
foreach($matches as $match) {
$counties[] = $match->county;
}
$results = DB::table('sales')->whereIn('county', $counties)->get();
Many Thanks for your help!
#update
Relationships:
Match:
public function people()
{
return $this->belongsTo('People', 'PeopleID', 'PeopleID');
}
People:
public function matches()
{
return $this->hasMany('Match', 'PeopleID', 'PeopleID');
}
I have a connection between those as Match holds people's "search" credentials. The first solution which you have provided works perfectly. Now, this solution filtered out sales by county which is a good move as now they need to be filtered by minimum and maximum price (minprice, maxprice) and other credentials such as brand_a and brand_b.
The idea of brand_a and brand_b:
Checkboxes are responsible for changing brand_a and brand_b value inside Matching. If these are checked the values inside Matching entity become '1'. If these are not checked they become '0' which means that sales don't have to be filtered out by those values.
Sales entity contains "Holdings" attribute. The value of "Holdings" can be brand_a or brand_b. Sales also contains "Price".
So, to make this clear:
Sale Entity contains: SaleID, Price, Holdings, County.
Holdings are values: brand_a or brand_b.
Price is just a number.
County is plain text.
Matching Entity contains: PeopleID, MinimumPrice, MaximumPrice, brand_a, brand_b, county.
PeopleID is a foreign key. We need to know which matching belongs to what user.
(there can be multiple matchings for one user depending on the number of counties chosen).
MinimumPrice and MaximumPrice are numbers.
brand_a and brand_b are the values (1 or 0) depending if the checkboxes were checked.
County is the name of a county.
Now, if person 1543 (peopleID = 1543) contains 3 matchings, each containing different search credentials.
1st:
PeopleID: 1543
MinimumPrice: 1000
MaximumPrice: 7000
brand_a: 0
brand_b: 1
county: county_a
2nd:
PeopleID: 1543
MinimumPrice: 2500
MaximumPrice: 10000
brand_a: 1
brand_b: 1
county: county_f
3rd:
PeopleID: 1543
MiniumPrice: 2000
MaximumPrice:9500
brand_a: 0
brand_b: 0
county: county_d
I need to match this data against the data that is inside the Sales. There can be over a 1,000 different sales with different prices etc. I just need to filter them and display Sales that are desired by the person based on person's matching.
I hope this better presents you the situation. Thanks.
In short, I belive you need to leverage Eloquent Relationships to easily retrieve the data you desire. Read up on relationships in the docs here: https://laravel.com/docs/4.2/eloquent#relationships.
I've made some assumptions so you may need to work the following into your actual setup. Also, I found it quite difficult to 100% understand your DB structure from your question, but from what I gather from your question your DB structure is like this:
User/Person *has many* Match
(Note: Name may be wrong, but you didn't mention what it's called in the question all I can see is the word "user" and "personId")
Match *belongs to* User/Person
Based on this I think you should set up your relationships like this:
User
class User extends Eloquent {
public function matches()
{
return $this->hasMany('Match');
}
//...
}
Match
class Match extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
//...
}
Then your code can look like this:
$user = User::find($id); // get our User with the Id (person id?)
$matches = $user->matches; // get all the matches
// or you could one line the above: $matches = User::find($id)->matches;
// get all the counties in the returned matches, could use pluck() method in higher version of laravel
$counties = [];
foreach($matches as $match) {
$counties[] = $match->county;
}
$results = DB::table('sales')->whereIn('county', $counties)->get();
A better approach to this issue (I think) would be to give County it's own entity then Match would have a county_id then you can use a has many through relationship if you can link up Match, County and Sales. You can read more about has many through in the docs here: https://laravel.com/docs/4.2/eloquent#has-many-through
Also, a side point... this part of your code:
$sales->where('county', '=', $county);
will just continuously add where statements to your query which I would imagine won't return anything if there's more than one.
Just to make this clearer, imagine you have 2 counties "county_1" and county "county_2", through your for loop your query would end up like this:
WHERE COUNTY = "county_1" // first loop
AND COUNTY = "county_2" // second loop
and as you can see a match cannot be two counties at one time! So you were probably looking for ->orWhere('county', 'county_value') after the first one was added, but a better approach is to use whereIn('county', $countiesArray) which you can pass an array you've built up, which is what I've done above.
Hope this helps! Let me know if it wasn't clear.
Edit
The best approach would be to establish relationships between the Sale and Match entities. As I still don't fully understand your database schema I can't advise so well on how you would approach that. If you gave some more details it may be possible.
Alternatively, you could approach the code by building up an array which you will use for applying conditions to your query. Consider your updated question with the four if statements, anytime you're repeating yourself like that, more often that not it can be simplified.
$filters = [
'maxprice' => $match->maxprice,
'minprice' => $match->minprice,
'brand_a' => $match->brand_a,
'brand_b' => $match->brand_b,
];
$salesQuery = DB::table('sales');
foreach($filters as $key => $value) {
if($value) {
$salesQuery->where($key, $value);
}
}
$results = $salesQuery->get();
As your conditionals are a bit stricter in your code from your question, you do to it like this instead:
foreach($filters as $key => $value) {
if ($value) {
if (in_array(['brand_a', 'brand_b'], $key)) {
$salesQuery->where($key, $value);
} else if($key === 'minprice') {
$salesQuery->where($key, '>=' $value);
} else if($key === 'maxprice') {
$salesQuery->where($key, '<=' $value);
}
}
}
the good thing about this approach is that you can easily add new conditionals via the filters array without having to write a new if statement and query/where code each time.
I'll stress this probably isn't the best approach, ideally you'd leverage Eloquent Relationships, but it may be a starting point if you can't figure that out right away.

SQL Sum on single field with cakePHP don't work with paginate()

I need to get the sum of the field "valor", from the table "orcamentos".
I'm using this and it is working, but I know that this is not the right way:
//function index() from OrcamentosController.php
$orcamentoSubprojetosTotal = $this->Orcamento->query(
"SELECT
SUM(Orcamento.valor) AS TotalOrcamentoSuprojetos
FROM
orcamentos AS Orcamento
WHERE
Orcamento.subprojeto_id IS NOT NULL;"
);
$this->set(compact('orcamentoSubprojetosTotal'));
I have found this question cakephp sum() on single field (and others sum() function in cakephp query, using virtual fields to sum values in cakephp), but in the moment I add this line to my controller:
$this->Orcamento->virtualFields['total'] = 'SUM(Orcamento.valor)';
The paginate() stops working and display only one entry, like so:
Page 1 of 1, showing 1 records out of 2 total, starting on record 1, ending on 2
This is my index() function:
public function index($tipoOrcamento = null) {
$this->Orcamento->recursive = 0;
/*
$orcamentoSubprojetosTotal = $this->Orcamento->query(
"SELECT
SUM(Orcamento.valor) AS TotalOrcamentoSuprojetos
FROM
orcamentos AS Orcamento
WHERE
Orcamento.subprojeto_id IS NOT NULL;"
);
$this->set(compact('orcamentoSubprojetosTotal'));
*/
$this->set(compact('tipoOrcamento'));
if($tipoOrcamento == 'subtitulo'){
$this->set('orcamentos', $this->Paginator->paginate('Orcamento', array('Orcamento.subtitulo_id IS NOT NULL')));
}elseif($tipoOrcamento == 'subprojeto'){
$this->set('orcamentos', $this->Paginator->paginate('Orcamento', array('Orcamento.subprojeto_id IS NOT NULL')));
}else{
$this->set('orcamentos', $this->Paginator->paginate('Orcamento'));
}
}
Can I use the query() or someone can help me with the virtual field?
Thank you.
Do not use a virtual field
A virtual field is intended for things like:
public $virtualFields = array(
'name' => 'CONCAT(User.first_name, " ", User.last_name)'
);
If a virtual field is used from something which does not belong to the/a single row - it will not do what you're expecting as evident by the secondary effects on the find call paginate generates.
Use field
Instead, use the field method and pass in the expression:
$integer = $Model->field(
'SUM(valor)',
array('NOT' => array('subprojeto_id' => null))
);
Which will execute:
SELECT SUM(valor) from x where NOT (subprojecto_id IS NULL);
This will also return a scalar value, whereas calling query as shown in the question will return a nested array.
of course when you use SUM you'll get a single record
there are two things you can do:
create the virtualField just before the find() call and unset it just after the query.
using 'fields' => arra(...) in your paginator setting and list just the fields you need to retrieve and not the virtualField when you don't want to SUM

grabbing the last last id based on user_name - codeigniter

I'm working on a project that has one main article and you can attach many different articles to it. So on submit I have broken the article from into two different sections, since I need them to submit into two different tables.
After submitting the first part, I'm trying to get the last submitted id based on their user_name so I can attach the rest of the article. If that makes sense.
I've tried several different things, but nothing seems to grab back that id.
First I tried the insert_id() meathod, but that returns a 0
public function pullLastStoryId($author){
$this->db->where('author', $author);
$this->db->insert_id();
$story = $this->db->get('story_tbl');
return $story->result();
}
So then I also tried just grabbing
public function pullLastStoryId($author){
$story = $this->db->query('SELECT LAST_INSERT_ID() INTO story_tbl;')
return $story->result();
}
Any idea's
My guess is that you are talking about a foreign-key relationship between two tables. One way to do this would be something like:
$this->db->insert('author',$vals);
$id = $this->db->insert_id();
// do something with ID.
From what I can tell, you would probably then do something like this:
$story_row = $this->db->get_where('story_tbl',array('id' => $id));
You should not have to worry about different user_name property, because that will be last created ID. If you really wanted to get that row back, though, you could do this:
$author_row = $this->db->get_where('author',array('author_id' => $id));
If you need to have this ID somewhere else (say, after another form is submitted), you can either use a hidden form input or (this is a bit more secure), you can store that ID in session.
I am not clear with the question. Are you looking for something like this?
public function pullLastStoryId(){
$story = $this->db->query('SELECT id from tablename where id = LAST_INSERT_ID()')
return $story->result();
}
OR
public function pullLastStoryId(){
$id = $this->db->insert_id();
$this->db->where('id',$id);
$story = $this->db->get('story_tbl');
return $story->result();
}
If you want to get Last id, your way is wrong. $this->db->insert_id() or etc for active query. Try this: SELECT id FROM table ORDER BY id DESC LIMIT 1

Symfony app - how to add calculated fields to Propel objects?

What is the best way of working with calculated fields of Propel objects?
Say I have an object "Customer" that has a corresponding table "customers" and each column corresponds to an attribute of my object. What I would like to do is: add a calculated attribute "Number of completed orders" to my object when using it on View A but not on Views B and C.
The calculated attribute is a COUNT() of "Order" objects linked to my "Customer" object via ID.
What I can do now is to first select all Customer objects, then iteratively count Orders for all of them, but I'd think doing it in a single query would improve performance. But I cannot properly "hydrate" my Propel object since it does not contain the definition of the calculated field(s).
How would you approach it?
There are several choices. First, is to create a view in your DB that will do the counts for you, similar to my answer here. I do this for a current Symfony project I work on where the read-only attributes for a given table are actually much, much wider than the table itself. This is my recommendation since grouping columns (max(), count(), etc) are read-only anyway.
The other options are to actually build this functionality into your model. You absolutely CAN do this hydration yourself, but it's a bit complicated. Here's the rough steps
Add the columns to your Table class as protected data members.
Write the appropriate getters and setters for these columns
Override the hydrate method and within, populate your new columns with the data from other queries. Make sure to call parent::hydrate() as the first line
However, this isn't much better than what you're talking about already. You'll still need N + 1 queries to retrieve a single record set. However, you can get creative in step #3 so that N is the number of calculated columns, not the number of rows returned.
Another option is to create a custom selection method on your TablePeer class.
Do steps 1 and 2 from above.
Write custom SQL that you will query manually via the Propel::getConnection() process.
Create the dataset manually by iterating over the result set, and handle custom hydration at this point as to not break hydration when use by the doSelect processes.
Here's an example of this approach
<?php
class TablePeer extends BaseTablePeer
{
public static function selectWithCalculatedColumns()
{
// Do our custom selection, still using propel's column data constants
$sql = "
SELECT " . implode( ', ', self::getFieldNames( BasePeer::TYPE_COLNAME ) ) . "
, count(" . JoinedTablePeer::ID . ") AS calc_col
FROM " . self::TABLE_NAME . "
LEFT JOIN " . JoinedTablePeer::TABLE_NAME . "
ON " . JoinedTablePeer::ID . " = " . self::FKEY_COLUMN
;
// Get the result set
$conn = Propel::getConnection();
$stmt = $conn->prepareStatement( $sql );
$rs = $stmt->executeQuery( array(), ResultSet::FETCHMODE_NUM );
// Create an empty rowset
$rowset = array();
// Iterate over the result set
while ( $rs->next() )
{
// Create each row individually
$row = new Table();
$startcol = $row->hydrate( $rs );
// Use our custom setter to populate the new column
$row->setCalcCol( $row->get( $startcol ) );
$rowset[] = $row;
}
return $rowset;
}
}
There may be other solutions to your problem, but they are beyond my knowledge. Best of luck!
I am doing this in a project now by overriding hydrate() and Peer::addSelectColumns() for accessing postgis fields:
// in peer
public static function locationAsEWKTColumnIndex()
{
return GeographyPeer::NUM_COLUMNS - GeographyPeer::NUM_LAZY_LOAD_COLUMNS;
}
public static function polygonAsEWKTColumnIndex()
{
return GeographyPeer::NUM_COLUMNS - GeographyPeer::NUM_LAZY_LOAD_COLUMNS + 1;
}
public static function addSelectColumns(Criteria $criteria)
{
parent::addSelectColumns($criteria);
$criteria->addAsColumn("locationAsEWKT", "AsEWKT(" . GeographyPeer::LOCATION . ")");
$criteria->addAsColumn("polygonAsEWKT", "AsEWKT(" . GeographyPeer::POLYGON . ")");
}
// in object
public function hydrate($row, $startcol = 0, $rehydrate = false)
{
$r = parent::hydrate($row, $startcol, $rehydrate);
if ($row[GeographyPeer::locationAsEWKTColumnIndex()]) // load GIS info from DB IFF the location field is populated. NOTE: These fields are either both NULL or both NOT NULL, so this IF is OK
{
$this->location_ = GeoPoint::PointFromEWKT($row[GeographyPeer::locationAsEWKTColumnIndex()]); // load gis data from extra select columns See GeographyPeer::addSelectColumns().
$this->polygon_ = GeoMultiPolygon::MultiPolygonFromEWKT($row[GeographyPeer::polygonAsEWKTColumnIndex()]); // load gis data from extra select columns See GeographyPeer::addSelectColumns().
}
return $r;
}
There's something goofy with AddAsColumn() but I can't remember at the moment, but this does work. You can read more about the AddAsColumn() issues.
Here's what I did to solve this without any additional queries:
Problem
Needed to add a custom COUNT field to a typical result set used with the Symfony Pager. However, as we know, Propel doesn't support this out the box. So the easy solution is to just do something like this in the template:
foreach ($pager->getResults() as $project):
echo $project->getName() . ' and ' . $project->getNumMembers()
endforeach;
Where getNumMembers() runs a separate COUNT query for each $project object. Of course, we know this is grossly inefficient because you can do the COUNT on the fly by adding it as a column to the original SELECT query, saving a query for each result displayed.
I had several different pages displaying this result set, all using different Criteria. So writing my own SQL query string with PDO directly would be way too much hassle as I'd have to get into the Criteria object and mess around trying to form a query string based on whatever was in it!
So, what I did in the end avoids all that, letting Propel's native code work with the Criteria and create the SQL as usual.
1 - First create the [get/set]NumMembers() equivalent accessor/mutator methods in the model object that gets returning by the doSelect(). Remember, the accessor doesn't do the COUNT query anymore, it just holds its value.
2 - Go into the peer class and override the parent doSelect() method and copy all code from it exactly as it is
3 - Remove this bit because getMixerPreSelectHook is a private method of the base peer (or copy it into your peer if you need it):
// symfony_behaviors behavior
foreach (sfMixer::getCallables(self::getMixerPreSelectHook(__FUNCTION__)) as $sf_hook)
{
call_user_func($sf_hook, 'BaseTsProjectPeer', $criteria, $con);
}
4 - Now add your custom COUNT field to the doSelect method in your peer class:
// copied into ProjectPeer - overrides BaseProjectPeer::doSelectJoinUser()
public static function doSelectJoinUser(Criteria $criteria, ...)
{
// copied from parent method, along with everything else
ProjectPeer::addSelectColumns($criteria);
$startcol = (ProjectPeer::NUM_COLUMNS - ProjectPeer::NUM_LAZY_LOAD_COLUMNS);
UserPeer::addSelectColumns($criteria);
// now add our custom COUNT column after all other columns have been added
// so as to not screw up Propel's position matching system when hydrating
// the Project and User objects.
$criteria->addSelectColumn('COUNT(' . ProjectMemberPeer::ID . ')');
// now add the GROUP BY clause to count members by project
$criteria->addGroupByColumn(self::ID);
// more parent code
...
// until we get to this bit inside the hydrating loop:
$obj1 = new $cls();
$obj1->hydrate($row);
// AND...hydrate our custom COUNT property (the last column)
$obj1->setNumMembers($row[count($row) - 1]);
// more code copied from parent
...
return $results;
}
That's it. Now you have the additional COUNT field added to your object without doing a separate query to get it as you spit out the results. The only drawback to this solution is that you've had to copy all the parent code because you need to add bits right in the middle of it. But in my situation, this seemed like a small compromise to save all those queries and not write my own SQL query string.
Add an attribute "orders_count" to a Customer, and then write something like this:
class Order {
...
public function save($conn = null) {
$customer = $this->getCustomer();
$customer->setOrdersCount($customer->getOrdersCount() + 1);
$custoner->save();
parent::save();
}
...
}
You can use not only the "save" method, but the idea stays the same. Unfortunately, Propel doesn't support any "magic" for such fields.
Propel actually builds an automatic function based on the name of the linked field. Let's say you have a schema like this:
customer:
id:
name:
...
order:
id:
customer_id: # links to customer table automagically
completed: { type: boolean, default false }
...
When you build your model, your Customer object will have a method getOrders() that will retrieve all orders associated with that customer. You can then simply use count($customer->getOrders()) to get the number of orders for that customer.
The downside is this will also fetch and hydrate those Order objects. On most RDBMS, the only performance difference between pulling the records or using COUNT() is the bandwidth used to return the results set. If that bandwidth would be significant for your application, you might want to create a method in the Customer object that builds the COUNT() query manually using Creole:
// in lib/model/Customer.php
class Customer extends BaseCustomer
{
public function CountOrders()
{
$connection = Propel::getConnection();
$query = "SELECT COUNT(*) AS count FROM %s WHERE customer_id='%s'";
$statement = $connection->prepareStatement(sprintf($query, CustomerPeer::TABLE_NAME, $this->getId());
$resultset = $statement->executeQuery();
$resultset->next();
return $resultset->getInt('count');
}
...
}

Categories