Doctrine2: Map Object with Sub-Entities to Entity without additional table - php

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 ?

Related

Laravel many to many with a pivot relationship.

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.

Fetching all pivot table columns via custom pivot type

I have a many-to-many relationship where the pivot table has about 20 additional columns. I am using a custom pivot class, and I have successfully set up the code to return an instance of that class when the ->pivot property is accessed on the relation, e.g.
$supplier->products->pivot returns the custom pivot class.
However, when wanting to access the data, I can manually define all the individual attributes of the pivot class (which extends Pivot by the way) in the belongsToMany relationship like this:
return $this->belongsToMany(Product::class, ['prop1', 'prop2', 'prop3'])
...But, how can I retrieve all the pivot data of the class without manually defining them as it ties the relationship declaration very close to the class? Is this possible. If not, it's going to make maintainability a PITA! Ideally, it'd be really nice if withPivot just had a flag to get it all!
In my circumstances, I found it easier to separate all the data into a separate table and model, and add a foreign key in the pivot table to the additional table record. This allows me to use the 'normal' model handling in Laravel and means I don't have to mess around with problems like this!
My use case was a schema of product and supplier with a many-to-many, and each supplier having their own data for the product, namely price, stock, shipping cost / times etc, so I moved all this from the pivot to a SupplierProduct model.
I'll leave this question here, as although this isn't the direct answer to the question (which I fear the answer is 'no'), this is a solution which is viable and can save quite a bit of coding frustration!

How to implement CRUD with many:many relationship

I'm new to atk4, but I couldn't find some simple examples for using CRUD with many:many relationships between tables.
I read in some instructions that M:M is best implemented with intermediate table, which seems logical.
For example
Model ONE has:
$this->hasMany('Table1Table2','table1_id');
Model TWO has:
$this->hasMany('Table1Table2','table2_id');
And Intermediate Model (Table1Table2) has:
$this->hasOne('Table1');
$this->hasOne('Table2');
Which generates this table, which is OK:
create table table1table2 (
id int auto_increment not null primary key,
table1_id varchar(255),
table2_id varchar(255));
But how to implement CRUD? - how to implement listings, adding new, edit etc..
When on page I simply insert like this:
$this->add('CRUD')->setModel('Table1');
There is no relationship generated... It would be nice that user could select (on add and edit of table1) values from table2.
From reading and watching tutorials I have idea, but maybe is totally overwork, so I'm really asking what is the best way at ATK4 for this problem?
My idea:
add multiple dropdown to edit and add form and populate it with table2 values. If in edit, check in intermediate table for what is already checked.
extend basic CRUD, on formSubmitSuccess insert selected values from dropdown to intermediate table
do it differently for edit/add new..
later, do similar check for GRID&other stuff... can generate a lot of extra work..
So, I see its doable, but I'm sure M:M relationships are very common (it's basically two 1:M), so maybe is there much better solution?
No doubts you'll need intermediate table in relational DB design.
Question is, how you define Models. One solution is, as you already explained yourself, with 3 models. But you can also do something similar with just two models and using joins in your model definitions to join them directly to intermediate DB table.
Idea here is - Model is not the same as DB table in general. Model is something more than DB table. One model can join up like 10 tables and do something fancy with them :)
I guess there is no nice out-of-the-box solution for such interface (View) which will fit all needs. But in some cases, if you only need to set links (yes/no) between two tables, you can use form + grid + grid->addSelectable() or form + crud + crud->grid->addSelectable(). With such construct you can, for example, associate multiple user roles to users or Apps to Admins or favorite colors to people etc.
If you have more data fields in intermediate table than just linking id's, then you'll have to come up with some custom code. But I guess you can still take a peek on grid->addSelectable() method to grab some idea.
P.S. Sorry, this time I have no ready-to-use example :)
You need to create proxy table and create one to many relations between three tables
class Model_Admin extends Model_Table {
function init() {
parent::init();
$this->haveMany('AdminApp');
}
}
class Model_App extends Model_Table {
function init() {
parent::init();
$this->haveMany('AdminApp');
}
}
// this is proxy table
class Model_AdminApp extends Model_Table {
function init() {
parent::init();
$this->haveOne('Admin');
$this->haveOne('App');
}
}
actually there is no other way to create many-to-many relations

Model inheritance in Laravel 4

Groundwork
I have a database design (snipped to relevant portions) like so:
records
entities
people
where no proper relationship exists between the tables, but rather records is extended by entities is extended by people by sharing the same id.
I have my Eloquent models similarly defined so that Record is extended by Entity is extended by Person.
Goal
Record and Entity are abstract and I want to simply use Person and other models at that level in the hierarchy.
Problem
How do I 'link' Record and Entity to Person to make this all work seamlessly? I don't think relationships is appropriate. I assume it's related to something like this:
protected __construct() {
parent::__construct();
}
I suggest you still create a model for Person, Entity, and Record, and link them together with "belongsTo" relationships. (add entity_id column to "People" table, add "record_id" to "Entities" table).
If you are only working with the Person model, you could add a "saved" model observer to create the entity and record entries whenever you create a Person, and tie them all together with a global id.
http://laravel.com/docs/eloquent#model-observers
If you did it this way, you could access the person's entity and record field by doing something like:
$person->entity->fieldName
$person->record->fieldName
All that being said, this seems way too complicated. I would only have one table for People and store all the person's data there. imo.

Difference between HABTM relationship and 2 $belongsTo relationship with a third model

I'm creating a project management system which projects are assigned to users
What's the difference between creating a Model ProjectsUser and defining 2 $belongsTo relationship and defining HABTM relationships in both Project and User models? What would be the most correct way, though? And how do I save the data in the projects_users table?
From my experience, if you want to be able to save or delete rows only from the join table (the one with 2 IDs), then it is much more simple using three models associated through both a hasMany and a belongsTo association.
You can also retrieve data from the join table directly and do the queries you want much more easily
This is what CakePHP documentation says refering to HABTM and saving data:
However, in most cases it’s easier to make a model for the join table and setup hasMany, belongsTo associations as shown in example above instead of using HABTM association.
Here you can find more the full text:
http://book.cakephp.org/2.0/en/models/saving-your-data.html#what-to-do-when-habtm-becomes-complicated
I have used this method for a "reads" table (with post_id and user_id) as well as for subscriptions and similar kind of relationships.
The first way is called "hasAndBelongsToMany" [details here].
The second is called "hasMany through" [details here].
The second link relating to "hasMany through" has details and a lengthy explanation about when and why you would want to use it.
Not sure about the specifics of cakephp, but in general defining the relation model explicitly gives you more control over it, for instance if you wanted to do some validation or add callbacks on creation of this relationship.

Categories