CakePHP - Exclude Models from JOIN in find() method - php

I'm currently working on a project with CakePHP and its find() method to handle database queries.
My current situation is as follows:
I have 3 Models: User, Location and Order. The connection is that a User hasOne Location and User hasMany Orders.
When I'm using the find() method to get the User and the address (stored in the Location model) CakePHP returns the User, Location, and the Order Models. In my case, I don't need the Order information.
So my question is: Is it possible to tell CakePHP not to join with the Order Model?
I know about the recursive attribute, but if I set it to -1, CakePHP returns just the User Model and in the case of recursive >= 0, it returns all 3 Models.

Solved problem with Containable of cakephp. The code is as follows:
$this->User->Behaviors->load('Containable');
$this->User->recursive = -1;
$this->paginate = array('fields' => array('User.*'),
'contain' => array('Location'=>array('field1', 'field2')
);
$user = $this->Paginator->paginate('User');

You can use unbind model function to exclude desired models
In your case :
$this->User->unbindModel(array('hasMany' => 'Orders'));
For common
$this->User->unbindModel(
array(
'hasMany' => array('Model1','Model2'),
'hasOne' => array('Model1','Model2'),
'belongsTo' => array('Model1','Model2'),
)
);

Related

Laravel 6.x right way to handle Pivot tables

I want to connect my Customer model with my CustomerGroup model in Laravel using Pivot tables.
I tried it with the following: (as reference I used https://ben.lobaugh.net/blog/204838/how-to-use-custom-pivot-tables-in-laravel-6-x but I could have made something wrong)
I created the migration customer_group_customer and in the scheme I added following:
$table->unsignedBigInteger("customer_group_id");
$table->unsignedBigInteger("customer_id");
In the models Customer and Customer_groups I added a function. The function is like the following (this is the Customer model for example):
public function groups(){
return $this->belongsToMany("App\CustomerGroup","customer_group_customer","customer_id","customer_group_id");
}
Then I created a customer and a Group and then connected them manually with:
DB::table("customer_group_customer")->insert(["customer_group_id" => $group->id, "customer_id" => $customer->id]);
After that I fetched all Customers and saw that there aren't connected (via dd() I couldn't see any entry on groups or similar):
$customer = \App\Customer::create([]);
$group = \App\CustomerGroup::create(["name" => "hey"]);
DB::table("customer_group_customer")->insert(["customer_group_id" => $group->id, "customer_id" => $customer->id]);
dd(\App\Customer::first());
How do pivot tables get setup correctly?
And is there a better way to create a customer and assign it a group, without making it manually with the DB facade?
The reason your not seeing the groups relationship when you dd() the Customer is because it hasn't been loaded.
You can load the relationship by using with() when querying the model, or load() after you have an instance of the model, or simply by accessing the relationship as a property e.g. $customer->groups;
Eloquent Eager loading documentation
You can also use the relationship to attach different relations rather than using the DB facade to do it manually:
$customer = \App\Customer::create([]);
$group = \App\CustomerGroup::create(['name' => 'hey']);
$customer->groups()->attach($group);
dd($customer->groups);
Attaching / Detaching Documentation

Yii1 search() by many-many relation returns only one related model

I have 3 tables:
clients, traders and client_trader_relation
clients can have many traders, and traders can have many clients so this is a MANY-MANY relationship with a "pivot" table. The relation is defined in clients model like this:
$relations = array(
'traders' => array(self::MANY_MANY, 'traders', 'client_trader_relation(client_id, trader_id)'),
);
Now everything works correctly when displaying a listing of all clients in let's say CGridView, but I also want to be able to search for clients by a specific trader (so if one of the traders is let's say id 10, then return this client).
I have done it like this in model's search() function:
public function search()
{
$criteria=new CDbCriteria;
$criteria->with = 'traders';
$criteria->together = true;
$criteria->compare('traders.id', $this->search_trader);
}
search_trader is an additional variable added to the model & rules so it cna be used for searching.
While this works, it successfully returns all clients of specified trader, the result doesn't contain any other related traders, just the one I'm searching for. I can understand this behaviour, because that's the way the generated SQL works.
I'm curious though if there is any way to return all the traders from such search without having to make any additional queries/functions? If not, then what would be the correct way of doing such thing? As for now, I can only think of some function in the model like getAllTraders() that would manually query all the traders related to current client. That would work, I could use this function for displaying the list of traders, but it would produce additional query and additional code.
You can use this to disable eager loading:
$this->with(['traders' => ['select' => false]]);
But this will create separate query for each row, so with 20 clients in GridView you will get extra 20 queries. AFAIK there is no clean and easy way to do this efficiently. The easiest workaround is to define additional relation which will be used to get unfiltered traders using eager loading:
public function relations() {
return [
'traders' => [self::MANY_MANY, 'traders', 'client_trader_relation(client_id, trader_id)'],
'traders2' => [self::MANY_MANY, 'traders', 'client_trader_relation(client_id, trader_id)'],
];
}
And then define with settings for eager loading:
$this->with([
'traders' => ['select' => false],
'traders2',
]);
Then you can use $client->traders2 to get full list of traders.
You can also define this relation ad-hoc instead of in relations():
$this->getMetaData()->addRelation(
'traders2',
[self::MANY_MANY, 'traders', 'client_trader_relation(client_id, trader_id)']
);

Cakephp - Multiple Models for one Table

I have a User model that is used to store data on users of a dental examination system.
Typically, three types of users will exist: Admininistrator, Location Manager and Examiner.
It seems to have become necessary to treat these three roles as seperate models in my application (imagine how I'd have a different view for each role with different options etc... It's a nightmare).
How would I go about setting up the relationships in each Model.
My first thought is:
//Administrator Model
class Administrator extends User {
$name = 'Administrator';
$table = 'User';
$belongsTo = array(
'User' => array(
'className' => 'User',
'conditions' => array('User.role' => 'administrator'),
)
);
}
And then the User model will reference this one using a hasMany? In terms of CakePHP convention, how would one actually model this accurately?
Also, would this model extend the User model or the AppModel?
Of course you can create different models using the same table name. To do so, link each model with specific table with $useTable property (not $table):
class Administrator extends AppModel {
public $useTable = 'User';
}
But I don't know any CakePHP model property which will allow you to filter data returned when fetching results... You can only write your own conditions when linking model with another one, for example:
class SomeOtherModel extends AppModel {
public $hasMany = array(
'Administrator' => array(
'className' => 'Administrator',
'conditions' => array('Administrator.role' => 'administrator')
)
);
}
Which is not a good solution, because it will work only when executing queries for SomeOtherModel model.
You can also try applying an afterFind() callback to your Administrator / Examiner / ... models which will delete users of another role than needed, but of course it is not an efficient solution.
Another solution is just creating different database views which will contain only users of selected role (more on views - here). Then you can point each view to your CakePHP model, just as it was a normal database table.
But this solution is also not perfect. If you will have to add a new user role or change your table schema in the future, you will also have to modify your database views.

how to get CakePHP many-to-many result

I have a many-to-many relation between the models Image and Units using a images_units join table.
How can I translate this query to a cakePHP find()?
SELECT * FROM Image, Units, images_units WHERE images_units.unit_id = 29;
Right now I'm trying find() on Image->find('all', $params); with no luck.
Straight from the CakePHP Manual:
$this->Image->bindModel(array('hasOne' => array('ImagesUnit')));
$this->Image->find('all', array('fields' => array('Image.*'),'conditions' => array('ImagesUnit.unit_id' => 29)));
Of course, you will need to have the HABTM association defined in the model. See the whole section on HABTM for learning how to use it.
In your Image Model, add the following code:
$hasAndBelongsToMany = 'Unit';
The find() in your Images controller should look like this:
$this->Image->find('all', array('conditions'=>array('Unit.id'=>29)));
Still not quite sure this is what you're looking for but I think this is correct.

adding hasMany association causes find() to not work well

OK, I am a little bit lost...
I am pretty new to PHP, and I am trying to use CakePHP for my web-site.
My DB is composed of two tables:
users with user_id, name columns
copies with copy_id, copy_name, user_id (as foreign key to users) columns.
and I have the matching CakePHP elements:
User and Copy as a model
UserController as controller
I don't use a view since I just send the json from the controller.
I have added hasMany relation between the user model and the copy model see below.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Without the association every find() query on the users table works well, but after adding the hasMany to the model, the same find() queries on the users stop working (print_r doesn't show anything), and every find() query I am applying on the Copy model
$copy = $this->User->Copy->find('all', array(
'condition' => array('Copy.user_id' => '2')
));
ignores the condition part and just return the whole data base.
How can I debug the code execution? When I add debug($var) nothing happens.
I'm not an expert, but you can start with the following tips:
Try to follow the CakePHP database naming conventions. You don't have to, but it's so much easier to let the automagic happen... Change the primary keys in your tabel to 'id', e.g. users.user_is --> users.id, copies.copy_id -->copies.id.
Define a view, just for the sake of debugging. Pass whatever info from model to view with $this->set('users', $users); and display that in a <pre></pre> block
If this is your first php and/or CakePHP attempt, make sure you do at least the blog tutorial
Make CakePHP generate (bake) a working set of model/view/controllers for users and copies and examine the resulting code
There's good documentation about find: the multifunctional workhorseof all model data-retrieval functions
I think the main problem is this:
'condition' => array('Copy.user_id' => '2')
It should be "conditions".
Also, stick to the naming conventions. Thankfully Cake lets you override pretty much all its assumed names, but it's easier to just do what they expect by default.
The primary keys should be all named id
The controller should be pluralised: UsersController
First off, try as much as possible to follow CakePHP convention.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Your association name is 'Copy' which is a different table and model then on your classname, you have 'Friendship'.
Why not
var $hasMany = array(
'Copy' => array('className'=>'Copy')
);
or
var $hasMany = array(
'Friendship' => array('className'=>'Friendship')
);
or
var $hasMany = array(
'Copy' => array('className'=>'Copy'),
'Friendship' => array('className'=>'Friendship')
);
Also, check typo errors like conditions instead of condition
Your table name might be the problem too. I had a table named "Class" and that gave cake fits. I changed it to something like Myclass and it worked. Class was a reserved word and Copy might be one too.

Categories