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
Related
I'm trying to implement a way to get the details of a person depending on the group it belongs to.
My database looks like this:
persons:
id
group
type
1
person
9
2
company
30
3
person
9
and so on.
Each "group" has a model which contains detail information for this record specific to the group.
For example:
persondetails looks like this
id
person_id
firstname
lastname
birthname
1
1
Harry
Example
Bornas
2
3
Henrietta
Example
Bornas
I created models for each table and I'm no trying to implement a relationship which allows me to query a person->with('details') via the person model (for example: for a complete list of all persons no matter which type it is).
For single records I got it working via a simple "if $this->group === person {$this->hasOne()}" relation, which doesn't work for listings.
I tried to wrap my head around a way to use a polymorphic relationship, so I put the following into the person model:
public function details(){
Relation::morphMap([
'person' => 'App\Models\Persondetail',
'company' => 'App\Models\Companydetail',
]);
return $this->morphTo();
}
and a subsequent
public function person(){
return $this->morphMany(Person::class, 'details');
}
which doesn't work sadly. Where is my thinking error?
As you're not using laravel convention for the keys, you need to define the keys on your relation
public function details()
{
Relation::morphMap([
'person' => 'App\Models\Persondetail',
'company' => 'App\Models\Companydetail',
]);
return $this->morphTo(__FUNCTION__, 'group', 'type');
}
Docs Link:
https://laravel.com/docs/9.x/eloquent-relationships#morph-one-to-one-key-conventions
Based on the reply by https://stackoverflow.com/users/8158202/akhzar-javed I figure it out, but I had to change the code a bit:
Instead of the code in the answer, I had to use the following:
public function details()
{
Relation::morphMap([
'person' => 'App\Models\Persondetails',
'company' => 'App\Models\Companydetail',
]);
return $this->morphTo(__FUNCTION__, 'group', 'id', 'person_id');
}
I am trying to make a testcase within laravel.
I have a fake User model (which dosent exists in DB), and creating it using faker->make,
and a real Role model which exists in DB,
these two have a many-to-many relationship
in my testcase, i am going to associate them like here :
public function testAccess()
{
$user = factory(\App\User::class)->make();
$supervisionControllerRole = \App\Role::where('name', 'supervision_controller')->first();
$user->roles->add($supervisionControllerRole);
}
since i dont want to save the relation in database, i am using add() instead of attach():
$user->roles()->attach($supervisionControllerRole->id);
//resulting database modification.
Problem
my problem is, when i am trying to get the relation from the model its ok.
var_dump($user->roles->first());
but when i am trying to get the relation Within The Model, it dosent works.
like here in my User Model:
public function hasRole($roleName)
{
$role_id = Cache::tags(['role_id'])->remember($roleName, 24*3600, function () use ($roleName) {
return \App\Role::where('name', $roleName)->first()->id;
});
return $this->roles()->where('role_id', $role_id)->exists();
}
It will returns false and trying $this->roles->count() results 0
from inside of the model.
My definitions
in User model:
public function roles()
{
return $this->belongsToMany("App\Role", "role_user")->whereNull("deleted_at")->using("App\RoleUser");
}
User Factory:
$factory->define(User::class, function (Faker $faker) {
return [
'id' => $faker->randomNumber(),
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => Str::random(80), // password
'remember_token' => Str::random(10),
];
});
Whenever you call a relationship with parentheses, such as
return $this->roles()->where('role_id', $role_id)->exists();
^^
you're accessing a Builder query instance which will return info from the database. But your data is not in the database, so of course it won't find anything when it looks there.
When you directly add() the relationship (vs attach()), you're inserting into a Collection instance, which as you know doesn't affect the database. This information is saved on the model only and stored in memory. Hence when you do
var_dump($user->roles->first());
it finds the information since it's already in memory. (You should also be able to call $user->roles->count() here and get a non-zero value.)
And since it's in a relationship of the model vs a direct attribute, I don't even think it would update the database if you were to save() the model.
You can use the contains method to perform the first step if you are not storing in the database:
return $this->roles->contains($role_id);
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 would like to know why the framework might have this strange behavior.
If I define the relation in my Event model as weird or any other name besides interest, it works properly getting an object of the class Interest.
public function relations()
{
return array_merge(
parent::relations(),
array(
'weird' => array(self::BELONGS_TO, 'Interest', 'interest_id'),
));
}
But if I change the name to interest it returns null
public function relations()
{
return array(
'interest' => array(self::BELONGS_TO, 'Interest', 'interest_id'),
);
}
So simply changing the name to interest means the relation will return null
Do you have any variables on the Event called interest you can't override native class variables with Yii specials like relations and magic methods. If not then something stranger is going on here.
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