I have a tables called users, countries, and countries_users.
The documentation states that to delete a simple relationship you perform:
// Get user foo
$u = new User();
$u->where('username', 'foo')->get();
// Get country object for Australia
$c = new Country();
$c->where('name', 'Australia')->get();
// Delete relation between user foo and country Australia
$u->delete($c);
This would remove the corresponding row from the countries_users table.
My question is, what if I have no relevant Country() object to construct?
If countries and users are a one-to-many relationship, then certainly knowing the username attribute is enough to disassociate him with a country.
All the delete functions seem to require at least two objects... What is the best way to accomplish the deletion of this type of relation using the DataMapper ORM functions?
"All the delete functions seem to require at least two objects"
Not totally true, a delete() can be preformed on a single object without the need to explicitly delete the object's relationships, it is handled automatically.
From the user guide:
Note: When you delete an object, all its relations to other objects will also be deleted. Free house cleaning! :)
In addition, you may use a column in the users table for the country id instead of a separate countries_users table for relationships, assuming it is a one(country)-to-many(users) relationship.
My question is, what if I have no relevant Country() object to construct?
Then you don't have to worry about anything. If there are no relationships to delete, attempting to delete them will not cause any harm.
There is a relationship to delete! I want to pass the user_id into my Controller and disassociate him with a country in the countries_users table. To accomplish this using the documented functions, I would have to also pass in the country_id... Which IMO is irrelevant for this operation.
You don't have to look up the country id unless you specifically want to delete a particular relationship. In your case, you're working with a relationship where a user can only have one country, so you don't need to specify which related country to delete. Here are two options off the top of my head:
Assigning a new country (removes the previous one)
$c = new Country();
// Get all countries named "Wonderland"
// Usually we'll use an id instead, there could theoretically be more than one
$c->where('name', 'Wonderland')->get();
$user->save($c);
Just delete all related countries (there is only one of course)
$c = new Country();
// Get all countries
$c->get();
$user->delete($c); // You may need $c->all here
If we were working with a many to many relationship, you would of course have to know which ones to delete, but since there is only one - deleting them all is sufficient.
Believe it or not, I was unable to remove the relationship using the code Wesley has provided.
However, this seemed to work:
$u = new User();
$u->where('id', $id)->include_related('country', 'id', TRUE, TRUE)->get();
$c = new Country();
$c->where('id', $u->country->id)->get();
$c->delete($u);
Related
As title, I have a table like student and another table is class.
User can edit about student having some class.
There is one to many relationship.
When user edit student's class, I always must to delete this student's class.
And insert the new class after that.
\App\class:where('student_id',$student->id)->delete();
foreach($request->input('class') as $class){
$new = new \App\class;
$new->student_id = $student->id;
$new->class_id = $class;
$new->save();
}
Is there have any solution about this issue?
Thanks.
For the moment I'll assume you have your relationships setup properly.
You'll want to take a look at sync.
Documentation
$student->classes()->sync($request->input('class'))
The sync method want's the ID's of the classes as argument.
User
uid
Provider
pid
Resolution
rid
ProviderResolution
prid
pid
rid
active
ProviderResolutionUser
prid
uid
class Provider extends Model {
public function resolutions()
{
return $this->belongsToMany('App\Models\Resolution')->withPivot('active')->withTimestamps();
}
}
class Resolution extends Model {
public function providers()
{
return $this->belongsToMany('App\Models\Provider')->withPivot('active')->withTimestamps();
}
}
class User extends Model
{
}
Trying to create a Eloquent relationship with this.
I'm trying to figure out how to fit user into this model. It seems like it's suppose to belongsToMany. Do I need to create a class that represents the pivot?
Then from the case of the User how would I query a list resolutions?
You didn't ask but I personally think it's a lot easier to let the primary key of each table be 'id.' Also, in the case of ProviderResolution, unless you have a specific case for it, you don't need (and shouldn't use) 'prid' at all. Just 'pid', 'rid' and 'active' should be sufficient. The 'pid' and 'rid' make the composite primary key on their own. If you add yet another key ('prid'), then there will be a three-key composite which will technically enable you to have duplicates with your other two primary keys. Yuck. Example: PRID:1, PID:1, RID:1, then PRID:2, PID:1, RID:1. Now you have duplicates but your record is technically still unique because of the PRID key. But, maybe you want it this way for some reason?
For the answer I'm going to assume you are using Laravel 5.4+.
So first off, you don't need a class for the pivot. And secondly, you are currently trying to create a relationship between the user and the existing pivot table between Provider and Resolution by creating a table called 'provider_resolution_user'. If you want to query resolutions for a user, just use the relationship methods which gives you access to the attributes on the pivot table and the related models/tables.
First, setup the 'hasMany' relationships in both classes: Users and Resolutions (Providers already has a relationship to Resolutions, so you can use that relationship if you want to see the related Provider.) Then you'll need a pivot table called 'resolution_user'. Put the 'uid' and the 'rid' in the table. Make the relationships to the corresponding foreign key fields to their parent tables.
Now you can access the relationship directly like:
$user->resolutions->rid (or whatever the attribute is you want)
The previous example assumes you have already created a way to insert records into the pivot table (resolution_user) that relate the user and the resolution together.
If you want to access one of the attributes on the pivot table, 'pivot' creates an object instance with it's own attributes (from the table). You can access it like this:
$user->resolutions->pivot->active;
Of course, these methods are chainable so if you just wanted to see the active resolutions, you could also add a ->where statement.
Hope that helps and wasn't too muddy. I'm happy to clarify any points if need be.
EDITED ANSWER:
Because what you want to do is to disable a row in the provider_resolution table and have that reflect on the correct user, then just create a relationship in both the User model and the Resolution model. So when you disable a row in provider_resolution (pid, rid, active), you can lookup the appropriate user to update by using the inverse relationship between resolution and user. This should give you the user that is assigned to that particular resolution/provider combination. If for some reason you do need to find the user based on a unique combination of the TWO: resolution AND provider, then we might need to talk about polymorphic relationships. Let me know.
I'm struggling for hours about this one...
I have a Buddy object (Entity) which holds (should hold) a Preference object. Inside this object I have several collections of sub-objects. For simplicity I'll just explain it with one single sub-object. Let's call it University. It's also an entity of its own.
Currently, Buddy and Preference have a One-to-One association with Buddy having a database column preference_id and Preference just an id and nothing else.
Preference and University have a Many-to-Many association, mapped by Doctrine with a buddy_preferences_universities table with preference_id and univeristy_id columns.
My goal is to establish an association between Buddy and University keeping the described class hierarchy but without the need of a useless preferences table, i.e. I still want to be able to issue $unis = $buddy->getPreference()->getUniversities and $unis being a University[] collection.
So, is it possible to "skip" the Preference entity and map the associations directly with the buddy_id?
Here's an image of the tables created by Doctrine's schema-tool:
If necessary I can also post the classes and/or XML mapping files.
Thank you!
Can you not make your buddy_preferences table like this:
id: char(36)
university_id: char(36)
country_id: char(36)
interest_id: char(36)
Like that you can drop your 3 join tables.
Now you can do $buddy->getPreference()->getUniversity();
How about remove the Preference entity of your model, then link with a Many2Many relation the Buddy and the University entities.
If you really need to keep the $buddy->getPreference()->getUniversities() function call to get all the Universities linked with a Buddy object, instead of $buddy->getUniversities()(which sound more logical in my mind, but however..), you can add a function in the Buddy class like this :
public function getUniversities()
{
return $this->universities;
}
// Now you'll be able to call $buddy->getPreference()->getUniversities()
public function getPreference()
{
return $this;
}
Althought it's a solution for your needs, I don't really think it's a good solution.
Why the $buddy->getPreference() is so important if you don't need a Preference object ?
So here's my problem.
I need to link an insurance policy to the insured property/item. Now the details vary greatly from car policy to a house or business one. So what I want to do is have something like this on the policies table
Policies
item_id
item_type
and that links to different tables depending on the value of the field "item_type" for example:
item_type = car then link to the cars table
item_type = house then link to the houses table
item_type = business then link to the businesses table
and so on...
I can do that on my own with php and mysql but I want to know the proper way to do it using CakePHP's table relationships and linking. I tried using the through option and a relationship table but it's not the same. Any ideas? or if a relationship table is the only way to do it then tell me how please.
This is actually a lot simpler than it first appears. I've done this a few times so I'll detail the technique that I use.
The first thing to do is create a behavior. This will allow you to enable any Table class in your application to have a policy attached to it.
The behavior is very simple. I've changed it to match your example, as I understand it. I'll talk through it after the code.
namespace App\Model\Behavior;
use Cake\Event\Event;
use Cake\ORM\Behavior;
use Cake\ORM\Query;
class PolicyBehavior extends Behavior
{
public function initialize(array $config)
{
parent::initialize($config);
$this->_table->hasMany('Policies', [
'className' => 'Policies',
'foreignKey' => 'table_foreign_key',
'bindingKey' => 'id',
'conditions' => ['table_class' => $this->_table->registryAlias()],
'propertyName' => 'policies'
]);
}
public function beforeFind(Event $event, Query $query, \ArrayObject $options, $primary)
{
$query->contain(['Policies']);
return $query;
}
}
So the in the initialize method we need to create a relationship to the table we attached the behaviour to. This will create a Table hasMany Policies relationship, meaning that any item in your system can have many policies. You can update this relationship to match how you're working.
You can see that there are a number of options defined in the relationship. These are important, as they link the tables items together. So the table_foreign_key is a field in your policies db table used to store the primaryKey of the related item. So if you're attaching a Policy to a Car, this would be the Car.id. The bindingKey is the key used in the Policy table to join on.
In order to filter the different types of attachments, you need the table_class field in your policies db table. This will be the name of the attached table class. So Cars, Cats, Houses etc. Then we can use this in the conditions, so anything pulling the primary table class will automatically filter the related Policies to match.
I've also configured the propertyName, this means that any item you look for which contains Policies will have an entity property called policies with the related data inside.
The last function in the behaviour is the beforeFind, this just ensures that whenever you look for the primary table class, you always return the related policies, you don't have to use this if you don't want to, but I found it handy to always have the related data in my use-case.
So then, how do we use this new behaviour? Just attach it like you would any other behaviour, and that's it. $this->addBehavior('Policy').
Be aware
This just reads data, you'll need to ensure that you save the table alias, and the foreignKey into the related table when creating new items.
Just for clarity, your policies table schema will need, at a minimum.
policies.id
policies.table_class VARCHAR(255)
policies.table_foreign_key INT(11)
Recently started working with OOP in PHP. Following the "code to an Interface" principle, i got confused as to the type hint to use when passing a single object or multiple as argument to a method.
Currently, i have a "Student" class - represents a row in my students table, i also have a "Students" class that holds multiple student objects in an array.
To fetch the profile of one student, i pass the Students object (holding a single student object) to the profile class. I set a Students type hint in the profile class.
Now i feel this is bad code as i have lines like this
student = new Students();
and students = new Students();
question is,
am i on the right path?
if i remove the Students class and work with Student alone, based on the principle, how do i pass multiple Student objects (assuming array) to the profile class if it accepts a Student type hint?
what options do i have?
Thanks.
If by Students you mean a collection of Student objects, perhaps a better name would be StudentCollection or StudentSet.
There are two ways around the type hint problem:
Introduce a method on StudentCollection called ->getProfiles(); it would return an array of profiles for each Student instance it's managing by calling methods on Profile.
Introduce a (static) method on Profile that operates on a StudentCollection instance.
The first option has feature envy, which is why I've included a workaround.
Instead of reinventing the wheel you might want to try Doctrine or at least take a look at its architecture.
I'm not sure if I get your exact issue... But if you want to go for your own code I would first abstract the DB layer as well and have some base classes like Database, Table, Row, Field that an describe the DB stack and extend them as needed with some magic methods. So when you do Student extends Table it would automatically check for a "students" table or whatever else convention you like to implement. Alternatively you could just pass the table name as arg.
Whatever Object is returning the result set from the database would have to construct a single Row object for each row and add it to a collection of rows that I would name ResultSet and contains all the row objects and return that collection.