CakePHP, find() with table alias - database

I want to copy 4 tables, deep associated:
Test->Scale->Question->Option
into
TestResult->ScaleResult->QuestionResult->OptionResult
In TestResult and OptionResult I want to add some new columns and also I need to have id of each 'has many' table to put it into each of 'belongs to' table.
So when I use find() I want to add aliases to result returned by Cake.

Yes, that is possible. You can provide the alias in the $hasMany, $belongsTo, etc.
public $hasMany = array(
'ImagesAlias' => array(
'className' => 'Image',
'foreignKey' => 'car_id'
)
);
You can read everything about it in the documentation

Related

cakephp - order by timestamp DESC not working

I have a table called posts that stores all the posts . It has two columns "Created" and "Modified" .
Below is my query in the model :
$options = [
'conditions' => [
'circle_id' => $my_circle_list,
'team_id' => $this->current_team_id,
'modified BETWEEN ? AND ?' => [$start, $end],
],
'order' => ['modified'=> 'desc'],
'limit' => $limit,
'fields' => ['post_id'],
];
$res = $this->find('list', $options);
Now i want the latest edited posts on top and below is what my mysql dump reads like :
SELECT `Post`.`id` FROM `db`.`posts` AS `Post` WHERE `Post`.`id` IN (125, 124) AND `Post`.`del_flg` = '0' ORDER BY `Post`.`modified` desc LIMIT 20
If i run this query in my database editor ,it gives my the correct output , but in my controller the ordering changes again , this is something i figured after i debugged the array values.
Would be helpful if anyone could tell me if there's any specific reason behind this . The call to this model method from the controller is done in a conventional manner .
When using $this->find() after Cake has queried the database with the generated SQL it calls the afterFind() method on the model where it can manipulate the data. This is the mostly likely place for the ordering to have been modified.

How to select a specific field additionally to a tables default fields?

I an looking to use a JOIN to select data from a table and a view in CakePHP like so :
$this->Annonces->find('all')
->where($arrFiltres)
->order($arrOrder)
->join([
'table' => 'annonces_suivis',
'alias' => 'AnnoncesSuivis',
'conditions' => [...],
]);
And would like to be able to select all the fields from the first table and som of the jointed table like so :
->select(['Annonces.*', 'AnnoncesSuivis.id']);
But this creates a faulty SQL query.
.* isn't supported by the ORM Query, it will convert this to
Annonces.* AS Annonces__*
which is invalid SQL. It would work with the lower level Database Query (Connection::newQuery()), which doesn't add aliases, however it won't return entities, so that's probably not what you want.
See Cookbook > Database Access & ORM > Database Basics > \Cake\Database\Connection::newQuery()
Pass a table object
As of CakePHP 3.1 you can pass table objects to Query::select(), which will cause all the fields of the table to be selected.
$this->Annonces
->find('all')
->select(['AnnoncesSuivis.id'])
->select($this->Annonces)
->join([
'table' => 'annonces_suivis',
'alias' => 'AnnoncesSuivis',
'conditions' => [ /* ... */ ],
])
->where($arrFiltres)
->order($arrOrder);
That way the AnnoncesSuivis.id field, and all fields of Annonces will be selected.
See Cookbook > Database Access & ORM > Query Builder > Selecting All Fields From a Table
Build the fields from the schema
That's what passing a table object will cause internally too, and it's also supported in CakePHP < 3.1.
$query = $this->Annonces->find('all');
$fields = $query->aliasFields(
$this->Annonces->schema()->columns(),
$this->Annonces->alias()
);
$query
->select(array_merge(['AnnoncesSuivis.id'], $fields))
->join([
'table' => 'annonces_suivis',
'alias' => 'AnnoncesSuivis',
'conditions' => [ /* ... */ ],
])
->where($arrFiltres)
->order($arrOrder);
This would also work for the fields option that can be passed to Table::find(), though you'd have to use a separate query object in that case, like
$fields = $this->Annonces->query()->aliasFields(
$this->Annonces->schema()->columns(),
$this->Annonces->alias()
);
$this->Annonces->find('all', [
'fields' => array_merge(['AnnoncesSuivis.id'], $fields)
// ...
]);
Use Query::autoFields()
In ealier CakePHP version, you could also make use of Query::autoFields(), which, when set to true, will automatically include the fields of the main table and possible containments.
See Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Passing Conditions to Contain
Auto selecting all fields is the default behavior until you set fields via Query::select(), in that case you'll have to explicitly enable Query::autoFields().
$this->Annonces
->find('all')
->select(['AnnoncesSuivis.id'])
->autoFields(true)
->join([
'table' => 'annonces_suivis',
'alias' => 'AnnoncesSuivis',
'conditions' => [ /* ... */ ],
])
->where($arrFiltres)
->order($arrOrder);
This should give you the desired query, however as mentioned this will only work for the main table and containments, if you'd wanted to include all fields of a manually joined table, then you'd have to specify them one by one.
You also can create virtual field in Entity:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity {
protected function _getFullName() {
return $this->_properties['first_name'] . ' ' . $this->_properties['last_name'];
}
}
echo $entity->full_name;

CakePHP 2.0 pagination and sum on hasMany-associated model

Project hasMany Comments
Payment (id, project_id, value, date)
I want to include in my index view a column equal to the sum of payments per project. I want to be able to sort by this field.
I've found some similiar problems here but neither of them concerns pagination.
My current (wrong) solution:
$this->paginate = array('Project' => array(
'conditions' => array('Project.archived =' => $archive),
'order' => 'Project.start_date DESC',
'contain' => array(
'Payment' => array(
'fields' => array('SUM(Payment.value) as Project__value_sum'),
'group' => array('Payment.project_id'),
)
)
));
$data = $this->paginate('Project');
$this->set('projects', $data);
$this->set('archive', $archive);
Seems like you'll probably want to do an afterSave on the Payment model.
Within the afterSave function, you run some MySQL that totals the payments (using MySQL SUM()) for the related Project, and store it in Project.payment_total.
Then, you can easily sort by that field, and it keeps you from having to run complex MySQL on every page, since it will only run when necessary (ie when there's a new or changed payment)

whats wrong with this query

I have this in my table model called Table
$test = $this->find('first', array(
'conditions' => array('table.test_id is NULL'),
'order'=> array('table.created ASC'),
)
);
it doesnt work. Tryingt to get the latest row with some criteria
Well, first of all, to get the latest row, you would want to organize by the created field descending, rather than ascending. Also, there are some problems with your syntax, that I have cleaned up below.
$this->find('first', array('conditions'=>array('Table.test_id'=>NULL), 'order'=>array('Table.created'=>'desc')));

CakePHP - HABTM - adding multiple tags to multiple points

I am trying to 'tag' multiple 'points' with multiple tags. I'm tagging my single points successfully. Unfortunately, when i try and use a tag, such as 'test2' on another point as a tag it is either giving me a duplicate entry error if i have my 'unique' set to false or if 'unique' is set to true, it will del my tag for all other points for 'test2' and create a single new one.
Here is what i have for my post data:
Array
(
[Tag] => Array
(
[id] => 4b7af6d7-787c-4f10-aa49-2502c0a80001
[name] => Test2
)
[Point] => Array
(
[id] => 4b47c66f-a130-4d12-8ccd-60824051e4b0
)
)
In my tag model i have this:
public $hasAndBelongsToMany = array(
'Point' => array(
'className' => 'Point',
'joinTable' => 'points_tags',
'foreignKey' => 'tag_id',
'associationForeignKey' => 'point_id',
'unique' => false)
);
I have tried this with 'unique' set as true, too. Unfortunately, this will delete any other instances of 'Test2' in the join table ('points_tags').
I have tried this using both save() and saveAll(). Both are giving me this error:
Warning (512): SQL Error: 1062: Duplicate entry '4b7af6d7-787c-4f10-aa49-2502c0a80001-4b47c66f-a130-4d12-8ccd-608' for key 'MAN_ADD' [CORE/cake/libs/model/datasources/dbo_source.php, line 527]
Query: INSERT INTO points_tags (tag_id,point_id,id) VALUES ('4b7af6d7-787c-4f10-aa49-2502c0a80001','4b47c66f-a130-4d12-8ccd-60824051e4b0','4b7b39f3-46f8-4744-ac53-3973c0a80001')
Thoughts????
Suggestions????
Where does the id come from? I'm guessing its a primary key of the table, and from what I understand from your post (please write more clearly, help us help you) the problem isn't with points or tags, but with the id in the points_tags table.
When you use the save method, are you doing it inside of a loop? Remember, best practice is to call model::create() whenever you're saving in a loop.
I frequently find that when I have issues with the HABTM saving behavior, it's because I didn't call model::create.

Resources