I have an application scheme that looks slightly like this:
<?php
class Category extends CActiveRecord
{
public function relations()
{
return array(
'posts' => array(self::HAS_MANY, 'Post', 'category_id'),
);
}
// ...
}
class Post extends CActiveRecord
{
public function relations()
{
return array(
'categories' => array(self::BELONGS_TO, 'Category', 'category_id'),
'pictures' => array(self::HAS_MANY, 'PostPicture', 'post_id'),
);
}
// ...
}
class PostPicture extends CActiveRecord
{
public function relations()
{
return array(
'post' => array(self::BELONGS_TO, 'Post', 'post_id'),
);
}
// ... public function deleteFiles() ...
}
All relations defined in PHP code also exist in the database with proper foreign keys and ON DELETE CASCADE set up (InnoDB). PostPicture provides a way to delete associated files.
When I delete a Category object via $category->delete();, ON DELETE CASCADE on the database level occurs, the picture records get deleted before I can access them and I won't be able to retrieve file system paths.
Completely disabling foreign keys is not very elegant - I would have to implement beforeDelete hooks for nearly every model class.
Retrieving all PostPicture rows associated to the Category's posts and calling their deleteFiles function in Category::beforeDelete() seems like an acceptable solution but is there a more elegant way to achieve this?
Isn't this the objective of onDelete cascade that related records get deleted. If you want to retrieve something then do it prior to delete. If I understand correctly you want to retrieve the filepaths so the files on the system can be deleted as well.
Since you know the records you are deleting find the data and save it and then execute the delete. just a thought.
oncascade is a database setting... so as the database is deleting the child records... the Yii or any PHP code does not "know" that... that's why your object is not deleted and beforeDelete/afterDelete is not called...
Copied from here
Related
Plugin: FriendsOfCake/Search
CakePHP: 3.1.4
I'm using the plugin to filter my index.ctp view data with a form.
This similar question:
How to Filter on Associated Data
is about a belongsTo association. My question is specifically about associated HABTM data where my associated table is linked through a joinTable and not directly. The normal setup in the Model like the following is not working in this case:
->value('painting', [
field' => $this->Paintings->target()->aliasField('id')
)]
My tables are set up like:
Tickets belongsToMany Paintings
Paintings belongsToMany Tickets
with joinTable tickets_paintings
Here is the main setup:
class TicketsTable extends Table
{
public function initialize(array $config)
{
...
$this->belongsToMany('Paintings', [
'foreignKey' => 'ticket_id',
'targetForeignKey' => 'painting_id',
'joinTable' => 'tickets_paintings'
]);
}
public function searchConfiguration()
{
$search = new Manager($this);
$search->value('status', [
'field' => $this->aliasField('active'),
])->like('member_name', [
'field' => $this->Members->target()->aliasField('surname'),
'filterEmpty' => true
])->value('painting', [
'field' => $this->Paintings->target()->aliasField('id'), // not working
]);
return $search;
}
class TicketsController extends AppController
{
public function index()
{
$query = $this->Tickets
->find('search',
$this->Tickets->filterParams($this->request->query))
->contain(['Members', 'Paintings', 'Appointments']);
...
}
Everything else is working and the parameters are added to the URL when I filter etc., so I only put in the parts where sth has to be wrong.
After filtering I get an error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Paintings.id' in 'where clause'
The contain works properly when just displaying data from the Paintings table in the Tickets view.
But in the code from the SQL query I can see, that all contained tables (Members, Appoinments) are joined for the query except the Paintings table, so obviously it can not find the column...And I guess it can't really join it directly anyway since they are only connected through the joinTable.
I'm new to CakePHP and I can't really figure out what I'm doing wrong here, so hopefully someone can help me out a bit.
Do I have to use a different syntax in the plugin settings? Do I have to set up my Tables differently? Or how exactly can I tell the query to incorporate the habtm related table in the search?
Thanks!
The available search methods rely on the field being available in the main query (hasMany and belongsToMany associations are being being retrieved in separate queries).
While you could join it in manually in the controller, using a callback- or a finder-filter is probably the better approach, that way you can modify the query in the model layer, and you could easily utilize Query::matching() to filter by associated data.
Here's an (untested) example that should give you a hint:
use Cake\ORM\Query;
use Search\Type\Callback; // This changed in master recently
// now it's Search\Model\Filter\Callback
// ...
public function searchConfiguration()
{
$search = new Manager($this);
$search
// ...
->callback('painting', [
'callback' => function (Query $query, array $args, Callback $type) {
return $query
->distinct($this->aliasField('id'))
->matching('Paintings', function (Query $query) use ($args) {
return $query
->where([
$this->Paintings->target()->aliasField('id') => $args['painting']
]);
});
}
]);
return $search;
}
See also
https://github.com/FriendsOfCake/search/blob/9e12117404f824847b2d1aa093f3d52736b658b4/README.md#types
https://github.com/FriendsOfCake/search/blob/master/README.md#filters
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Filtering by Associated Data
I am trying to creating a STAT relation for a model that sums up the contents of a column provided that another column matches my conditional statement. In my particular case, I need to get the sum of all the file sizes of the pictures that a user has uploaded.
Code:
class User extends CActiveRecord {
public function relations() {
return array(
'pictureSpaceUsed'=>array(self::STAT, 'Picture', 'user_id', 'select' => 'SUM(size)','condition' => 'user_id=' . $this->id),
),
}
}
Problem is that Yii complains that it can't access the id of the model. $this->id doesn't seem to be working inside the relations function... It works if I replace $this->id with a number though, but that wouldn't be dynamic anymore.
Anybody know what's going on here?
Why do you want to insert that condition? It looks like you already state the condition when you say:
class User extends CActiveRecord {
public function relations() {
return array(
'pictureSpaceUsed'=>array(self::STAT, 'Picture', 'user_id', 'select' => 'SUM(size)'),
),
}
}
It will find the relation based on user_id
There are 2 simple tables (MyIsam):
Child:
id(PK),
name,
land_id(FK)
Land:
id(PK),
name
These are the both Models (excerpt):
Following changes has no effects, if I modify the Models and let create the crud-forms, there are no changes and die land_id is getting no data from land table:
Model Child.php (excerpt)
class Child extends CActiveRecord
{
...
public function relations()
{
return array(
'land_id'=>array(self::BELONGS_TO, 'Land', 'id'),
);
}
...
}
Model Land.php
class Land extends CActiveRecord
{
...
public function relations()
{
return array(
'id'=>array(self::HAS_MANY, 'Child', 'land_id'),
);
}
...
}
Where is my mistake ?
EDIT: Do I need some more work, to get a select box with the corresponding land list in the created insert form (via CRUD) ?
thank you..
MySQL’s MyISAM engine does not support foreign keys at all.
Use InnoDB!
I am using Yii framework. i am wonder how i can get records from multiple tables i did research but couldn't find any usefull link i am using following code for this please let me know where i am missing
my model Task.php
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'prj_user' => array(self::BELONGS_TO, 'User', 'id'),
);
}
model User.php
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
array('task', self::HAS_MANY, 'Task','project_id')
);
}
and this is my main controller
$criteria = new CDbCriteria;
$criteria->compare('t.id', 1);
$criteria->with = array( 'prj_user' => array('select' => 'username,title,roles', 'joinType'=>'inner join'));
$rows = Task::model()->findAll( $criteria );
but still i am getting columns only from task table but i need more three columns from users table please help me
Let Yii worry about joining your tables. Your relations looks fine so you should be able to access them directly
For example, what does this return?
foreach ($rows as $task)
{
if ( isset($task->prj_user) )
echo $task->prj_user->username . "<br>";
}
Or this?
this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>new CActiveDataProvider('Task'),
'columns'=>array(
'id',
'prj_user.username',
'prj_user.title',
'prj_user.roles',
)
));
->with() is used for eager loading, so at this point you probably don't need it. In fact, unless I misread you completely, you can remove your criteria all together.
So I've tried this: http://www.yiiframework.com/wiki/285/accessing-data-in-a-join-table-with-the-related-models
Basically I have a table called User which relates to ToolAccess; related via a primary key on User and a field for userID on ToolAccess. Now tool access relates to the table Tool which contains a ToolID. Now this doesn't work in Yii, I can't seem to get the toolName field off of the tool table using Yii. Any ideas on how to do this on a Active Record?
I'm using giix if that matters.
Relations code:
public function relations() {
return array(
'usergalleries' => array(self::HAS_MANY, 'Usergallery', 'userid'),
'userinroles' => array(self::HAS_MANY, 'Userinroles', 'userid'),
'userfailedlogin' => array(self::HAS_MANY, 'Userfailedlogin','userid'),
// table name, relation, class name, relation key
'toolaccess' =>array(self::HAS_MANY, 'Toolaccess','userid'),
'tool' =>array(self::HAS_MANY, 'Tool','toolid')
);
}
I'm assuming your schema looks something like this:
User table tool_access table Tool table
id | other columns userid | toolid id | name | other columns
In this case, the User model should have a relation like this (note that the tools will be ordered by name in this case):
public function relations() {
return array(
// other relations here...
'tools' =>array(self::MANY_MANY, 'Tool', 'tool_access(userid,toolid)',
'order' => 'tools.name',
),
);
}
and the code to read the tools should look like this:
$user = User::model()->with('tools')->findByPk($id);
foreach($user->tools as $tool) {
echo $tool->name;
}
I used eager loading of the tools here mostly because of personal preference, using lazy loading should work just as well in this case. But eager loading should be preferred whenever you're processing multiple User records at once.
So if I have understood it properly, user and tool are related in a many-to-many relationship by their primary keys.
So you should define this relationship in the User model like:
'tools' => array(self::MANY_MANY, 'Tool', 'tool_access(userid, toolid)', 'index' => 'id'),
This way you can access the name of the tool after getting the user model
$user = User::model->findByPk($id);
$tools = $user->tools;
foreach ($tools as $tool)
{
echo $tool->name;
}
I hope it works for you.