HABTM with Pagination - php

At first take a look at the following model structure:
Model Building:
id
name
Model BuildingRange:
id
building_id
postalcode
Ok, so BuildingRange $belongsTo Building and Building $hasMany BuildingRange. Should be clear til' here.
Now let
$current_postalcode="12345";
I know want to do something like this in the BuildingController:
$this->paginate('Building',array('Building.BuildingRange.postalcode'=>$current_postalcode));
In text: I want to select all buildings for that an entry "BuildingRange" with $current_postalcode exists. How do you do that?
I appreciate your help!

When dealing with such a hasMany association, CakePHPs auto-magic needs two queries, one on the Building table, and one on the BuildingRange table. When passing conditions via the pagiante method, these conditions will be passed to the first query, and thus this it will fail since the associated models table isn't joined.
This problem can be solved on a few different ways, one would be using an ad-hoc join, for example:
$this->paginate = array
(
'joins' => array
(
array
(
'table' => 'building_ranges',
'alias' => 'BuildingRange',
'type' => 'LEFT',
'conditions' => array('BuildingRange.building_id = Building.id')
)
)
);
$this->paginate('Building', array('BuildingRange.postalcode' => $current_postalcode));
This would result in a query that looks something like this:
SELECT `Building`.`id`,
`Building`.`name`
FROM `buildings` AS `Building`
LEFT JOIN `building_ranges` AS `BuildingRange`
ON ( `BuildingRange`.`building_id` = `Building`.`id` )
WHERE `BuildingRange`.`postalcode` = '12345'
LIMIT 20
Note that in the conditions passed to the paginate method there is no need to reference the BuildingRange model through the Building model, ie no need to use Builduing.BuildingRange (that wouldn't work anyway).
ps, it's always good to mention the CakePHP version you are using!

Related

Cakephp subquery in paginate

I have an existing cakephp (version 2) controller index function doing this:
$options = ['Person.name LIKE' => $term];
$this->set('people', $this->Paginator->paginate($options));
resulting in a paginated table in the view.
My Person model references a child model of Appointment, where one person has many appointments like so:
public $hasMany = [
'Appointment' => [
'className' => 'Appointment',
'foreignKey' => 'person_id',
'dependent' => false
]
]
I now need to add a Person's Oldest Appointment Date column to my table, i.e. if working with raw SQL I might do this:
select
Person.id,
Person.name,
(select
min(Appointment.Date) from Appointment
where Appointment.person_id = Person.id
) as OldestAppointmentDate
from Person
where Person.name like 'foo%'
How can I modify the paginate() parameters so that this new field is included in the results and is sortable by paginate in the usual way?
The most simple way would probably be to use a virtual field, which you can then include in the paginators fields option, something like:
// in Model/Person.php
public $virtualFields = array(
'OldestAppointmentDate' => '
select
min(Appointment.Date)
from
Appointment
where
Appointment.person_id = Person.id
';
);
// in your controller action
$this->Paginator->settings['fields'] = array(
'Person.id',
'Person.name'
'Person.OldestAppointmentDate'
);
// ...
That will include the subquery and create the required aliases accordingly, and things get stitched together automatically so that the results look like as if OldestAppointmentDate is an actual field of Person, and you can refer to it in the paginator helper like any other field, ie:
$this->Paginator->sort('Person.OldestAppointmentDate');
See also
Cookbook > Models > Virtual fields
Cookbook > Core Libraries > Components > Pagination > Query Setup

How to get all rows from related table using join in Zend Framework

I have two tables. First table contain offer details and second table contains offer collection of languages(collection can be selected in main offer form). I want to use query with "join" language table to get all of them in single query. How it's look like:
Relation
Trying to get all of the offers I want to get all offer languages as one field.
array [
'id' => string '11',
'name' => string '134',
'date' => string '01-12-2016',
'languages' => array(all language value from related table)
]
Here is my query:
$select = $this->getDbTable()->select()
->setIntegrityCheck(false)
->from(array('o' => 'offers'), array('o.*'))
->joinLeft(array('ol' => 'offer_language'), 'ol.id_offer = o.id', array('ol.*'));
$resultSet = $this->getDbTable()->fetchAll($select)
In that way if one offer has a three language then I get three same offer with different language field.
Zend Version: 1.11
Thanks for help.
The solution is to use a Zend_Db_Expr with GROUP_CONCAT.
Here are two posts that will give you a clear explanation:
Can I concatenate multiple MySQL rows into one field? and How to use GROUP_CONCAT with Zend Framework?
Your code will end up looking something like this:
$select = $this->getDbTable()->select()
->setIntegrityCheck(false)
->from(array('o' => 'offers'), array('o.*'))
->joinLeft(array('ol' => 'offer_language'), 'ol.id_offer = o.id', array('languages' => new Zend_Db_Expr('GROUP_CONCAT(ol.language)')))
->group('o.id');
Good luck!

How to get data from Relations with joins in yii

I am beginner in Yii, So I am asking this question.
I have three different tables.
First Table
First table is language(id, language_name)
// id is primary key.
Second Table
Second Table is verse(id, topic_id, surah_id, verse_text)
// id is primary key,
Third Table
Third table is verse_translations(id, verse_id, language_id, translations_text)
// id is primary key, language_id is foreign key references with language table,
// verse_id is foreign key references with verse table.
Now My Question is.
I want to get the list of languages of available translations with specific verse_id. ? For that i want to make relations in verse model file that will return an available languages in my view, so how to get result in view also.? and what will be the changes in verse model, view and controller if any changes occur.
I have written MySQL query which is in below.
SELECT language.language_name from language
Inner Join verse_translations ON verse_translations.language_id = language.id
Where verse_translations.verse_id = 1
But i need this in Yii.
I have generated verse model through gii code generator.
My Verse Model Relations function.
public function relations()
{
return array(
'sorah' => array(self::BELONGS_TO, 'Sorah', 'sorah_id'),
'topic' => array(self::BELONGS_TO, 'Topic', 'topic_id'),
'verseFeedbacks' => array(self::HAS_MANY, 'VerseFeedback', 'verse_id'),
'verseImages' => array(self::HAS_MANY, 'VerseImages', 'verse_id'),
'verseLinks' => array(self::HAS_MANY, 'VerseLinks', 'verse_id'),
'verseTafseers' => array(self::HAS_MANY, 'VerseTafseer', 'verse_id'),
'verseTranslations' => array(self::HAS_MANY, 'VerseTranslations', 'verse_id'),
'language_name' => array(self::HAS_MANY, 'Language', 'id'),
);
}
I wrote you your sql code,
$result = Yii::app()->db->createCommand()
->select('l.language_name')
->from('language l')
->join('verse_translations vt' , 'l.id = vt.language_id ')
->join('verse v' , 'vt.id = v.id')
->where('v.id = :var' , array(':var'=>1))
->queryAll();
btw I didn't read all your post, just read your sql :D
UPDATE: if you define your relations in mysql before you generate model files, you get the relations generated for you.this is the easiest way possible, then you can do this:
$vers = Ver::model()->findByPk(1);
$allLangs = $vers->language_name; // this will give you an array of Language Model back
let me know what did
cheers
You can easily get the list of available translated languages from language table.
Let see first.
'verseTranslations' => array(self::HAS_MANY, 'VerseTranslations', 'verse_id'),
this relation will take all the rows of verse translation of specific verse id, mean if you have 10 different translation in 10 different languages with verse_id 1, it will display all. Now you can see in question verse_translation table have language_id.
So we can get all languages by that language_id.
Now we make another relation which is relating to language_id through verseTranslations, and this relation will display all the translated languages.
'verse_lang' => array(self::HAS_MANY, 'Language', array('language_id'=>'id'), 'through'=>'verseTranslations'),
So as i have written a Sql Query is equivalent to these two relations.
'verseTranslations' => array(self::HAS_MANY, 'VerseTranslations', 'verse_id'),
'verse_lang' => array(self::HAS_MANY, 'Language', array('language_id'=>'id'), 'through'=>'verseTranslations'),
On view, we can easily access it by var_dump($data->verse_lang)
That's it.
for understanding relations. You may read carefully to this link.
http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-through
Hope it will help.
If you need any help then leave a message in comment box.
Thanks.

get a single value from a table using the query() method inside a model

I have a very complex setup on my tables and achieving this via any of the find() methods is not an option for me, since I would need to fix relationships between my tables and I don't have the time right now, so I'm looking for a simple fix here.
All I want to achieve is run a query like this:
SELECT MAX( id ) as max FROM MyTable WHERE another_field_id = $another_field_id
Then, I need to assign that single id to a variable for later use.
The way I have it now it returns something like [{{max: 16}}], I'm aware I may be able to do some PHP on this result set to get the single value I need, but I was hoping there was already a way to do this on CakePHP.
Assuming you have a model for your table and your are using CakePHP 2.x, do:
$result = $this->MyTable->field('id', array('1=1'), 'id DESC');
This will return a single value.
see Model::field()
This example is directly from the CakePHP documentation. it seems you can use the find method of a model to get count
$total = $this->Article->find('count');
$pending = $this->Article->find('count', array(
'conditions' => array('Article.status' => 'pending')
));
$authors = $this->Article->User->find('count');
$publishedAuthors = $this->Article->find('count', array(
'fields' => 'DISTINCT Article.user_id',
'conditions' => array('Article.status !=' => 'pending')
));

CakePHP: Load count of related Models with bindModel

I have two models in an 1:n relation and I just want to load the count of the related items.
First one is the table/model "Ad" (one) which is related to "AdEvent" (many). AdEvents has a foreign key "ad_id".
In the controller I can use it that way and it loads the related AdEvent-records.
$this->Ad->bindModel(array('hasMany' => array(
'AdEvent' => array(
'className' => 'AdEvent',
'foreignKey' => 'ad_id',
))));
Now I just need the count without the data and I tried with param "fields" and "group" a COUNT()-statement, but in that case the result is empty. I also changed the relation to "hasOne", but no effect.
Any idea how to use the Cake-magic to do that?
EDIT:
With simple SQL it would look like this (I simplyfied it, a.id instead of a.*):
SELECT a.id, COUNT(e.id) AS count_events
FROM cake.admanager_ads AS a
JOIN ad_events AS e ON e.ad_id = a.id
GROUP BY a.id
LIMIT 50;
You can always do a manual count of course. This is what I almost always end up doing because I almost always have the data loaded already for some other purpose.
$Ads = $this->Ad->find('all')
foreach ($Ads as $Ad) {
$NumAdEvents = array(
$Ad['Ad']['id'] => sizeof($Ad['AdEvents']),
)
}
debug($NumAdEvents);
die;
Or you can use a find('count'):
$id_of_ad = 1; //insert your ad id here, or you can search by some other field
$NumAdEventsAtOneAd = $this->AdEvent->find('count', array('conditions' => array(
'AdEvent.ad_id' => $id_of_ad,
)));
debug($NumAdEventsAtOneAd);
die;

Categories