We are just switching to laravel in my workplace and I still have problems with using more complicated relationships.
I have the following models:
Company
id
name
...
Domain
id
url
company_id -> belongs to Company
...
CommunicationEvent
id
name (like: OrderPlaced)
...
Email
id
name
subject
body
company_id -> belongs to Company
...
SMS
similary to Email
Every Company should be able to set the communication to be used in an Event and in an event multiple type of communications can be used. For example, if an OrderPlaced event fires they can choose to have both an email and an SMS to be sent. Moreover they can set a default email/sms for all of their domains in an event and just overwrite it for one of their domains to use a different one there.
My first shot was to make a pivot table with event_id, company_id, domain_id(nullable for default) and communicable_id/type. I made a test relationship like this into CommunicationEvent:
public function mails(){
return $this->morphToMany('App\Models\Mail', 'communicable', 'comm_event_comms', 'event_id', 'communicable_id')->where('comm_event_comms.company_id', 1);
}
And I put dummy data into the table, but $event->mails returns empty. Reading the documentation made me think I'm not on the right track.
Honestly, I don't really know how to properly connect these models so I can use them easyly. The only way I could make it work was to use DB::where... queries, but nothing with relationships. :(
Can you please help to build a relationship between these models?
I realized what was the problem with the relationship. I should have used morphedByMany instead of morphToMany. Thanks to miken32 I reread documentation and realized this on 3rd read.
The right relationship in communicationEvent:
public function mails(){
return $this->morphedByMany('App\Models\Communication\Mail', 'communicable', 'communicables', 'event_id')->where('communicables.company_id', DData::getCompanyId());
}
I also changed the name of the pivot table to communicables for convention.
Like that $event->mails()->attach($mail_id, ['company_id' => DData::getCompanyId()]); works perfectly to set default email communication for event. And setting domain specific communication works as well with additional domain_id data in attach.
I can use $event->mails as well for select.
The only problem that remained is detaching. As I read here:
...if you're storing other data on a "pivot" it's not really a pivot
anymore...
I don't think I will be able to solve that with a simple detach() method so I used db queries like this:
DB::table('communicables')->where([
['event_id', $event->id],
['domain_id', NULL],
['company_id', DData::getCompanyId()]
])->delete();
I will have to search for communicable type as well when I connect the SMS model, but I decided it is a good enough solution. Other solution would have been to make a separate model for comminicables table, but I didn't wanted that.
Related
Suppose that I have a Notification Table that gets generated when a new log from another table is generated. Suppose I have 3 different logs with different purpose namely: sms_logs, call_logs, and appointment_logs.
I want to make a relationship to each logs without using sms_logs_id, call_logs_id and appointment_logs_id. Instead, I want to build only two columns, one for the type, and the other for the ID. So for example an sms log is generated with an id of 187, it will also generate a notification log with a notification_id of 187 and a type of "sms".
How will I be able to create that? Thank you!
Nice question.
You have to put only two fields in notifications table. foreign_id and log_type.
Whenever you add a log, you have to set log_type accordingly. Then add this relationship in your Notification model.
public function foreignModel()
{
switch($this->log_type){
case "call_log":
return $this->belongsTo('App\Call', 'foreign_id');
break;
}
}
I didn't tried it, but hope it will work fine.
If you are looking for something more dynamic and less robust than this then I don't think that it exists.
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 ?
I have a general question about how to implement the best practice of model structure for an application I'm building in Laravel 5.
So, at the moment I have things set up like this:
'user' model and table: id, email, password, admin level - this is really just the info for authenticating login.
'user-details' model and table: id, userID (foreign key for user table id field), name, address etc - all the other details
'lesson-type' model and table: id, teacherID (foreign key for user-details table id field), lesson-label etc - info about different types of lessons
At the moment I have a Teacher Controller in which I'm passing through to the view:
- The info from the User table
- The info from the User-details table
- A list of different lesson types for the teacher from the Lesson-type table
But I kind of feel that all this should be tied together with one separate Teacher model which would extend the User-details model (and probably which in turn should extend the User model), but wouldn't have it's own table associated with it, but all the info pertaining to either updates for the User-details or the Lesson-types table would be stored in those relevant tables. Would this be correct?
(I should also say that users may alternatively be parents rather than teachers, and so would I would have a separate Parents model for all the properties and so on associated with parents)
I would then pass only the Teacher model object into the view and thus gain access to all the teacher info such as personal details and array of lesson types.
As I'm typing, this is sounding more and more to me like the right way to go, but it would be great to get some advice.
1 - technical implementation: I guess in the Teacher model, I'd populate all the relevant teacher into class variables (Name, array of lessons etc) in the constructor?
2 - am I over complicating this structure by having both Users AND Users details tables?
3 - Does what I'm proposing make the most structural sense in Laravel?
4 - just another thought I've just had, should the teacherID in the lesson-type table actually refer to the User table rather than the User-detail table... so user-detail and lesson-type would both be direct children of the user table??
Very much obliged for any help :)
You shouldn't extend models like that unless there is a clear inheritance. From a logical standpoint, it just doesn't make any sense since you'll have to overwrite most of what is on the User model anyway. And what you don't overwrite will be incorrectly mapped to the database because they are 2 completely different tables. What you actually want to do is utilize Eloquent relationships.
For clarity, I am assuming this basic structure:
users - id
teachers - id, user_id
user_details - id, user_id
lesson_types - id, teacher_id
Those should be 4 completely different models all interconnected using the Model::belongsTo() method. So the Teacher model would be
class Teacher extends Model {
public $table = 'teachers';
public function user() {
return $this->belongsTo('App\User');
}
}
When you query for a teacher, you can do Teacher::with('user')->get(). That will return all records from the teachers table and on each instance of the Teacher model, you'll be able to call $teacher->user and get the User instance associated with that teacher. That is a full model, not just extra data, so you have access to everything on the User Model, which is generally the main reason for extending
For your list of questions:
I may be misunderstanding you, but this isn't how an ORM works. I'd suggest going back and reading through the Eloquent docs (if you're running 5.0, I suggest reading 5.1's docs since they are much, much better)
It will depend on who you ask, but I tend to think so. If the data is clearly related and there is no reason for it to be shared across record types (for example, I generally have an addresses table that all records reference instead of having 5 address fields repeated on multiple tables), I believe it should all be on one table. It just makes it more difficult to manage later on if you have it in separate tables.
There will be those who disagree and think that smaller scopes for each table are better and it will likely allow for quicker queries on extremely large datasets, but I don't think it's worth the extra trouble in the end
No, as I have explained above
The teacher_id column should reference the teachers table, assuming that lessons belong to teachers and cannot belong to just any user in the system. Using the ORM, you'll be able to do $lesson->teacher->user->userDetails to get that data
I really think you need to go back and read through the Eloquent docs. Your understanding of how Eloquent works and how it is meant to be used seems very basic and you are missing much of the finer details.
Basics
Relationships
Laracasts - Laravel Fundamentals - You would benefit from watching Lesses 7-9, 11, 14, and 21
I have 2 tables in my database, a notifications table and a notifications_user table.
A notification can have many notification users. And a result in my notification model I have the following relationship,
public function user()
{
return $this->belongsToMany('User')->withPivot('is_read');
}
What I am wanting to achieve is to get all notifications that are unread (or is_read = 0), the is_read column is in the notification user table though and I cannot work out how to run a query on it becuase of that.
Current I have this,
$unread = Notification::has('user')->with('user')->get();
Now this pulls the relationship into the results, but takes no account of the is_read value.
Is there a way to select all the data from a table based on a value in its 1:n relationship.
You can query on the relationship by doing something like:
$unread = Notification::has('user')->with('user')->where('is_read','!=', 0)->get();
Check the docs: http://laravel.com/docs/5.1/eloquent-relationships#querying-relations
So while it may make sense to have the relation that notifications have many users it really doesn't make sense from an ownership. Notifications are one-time things while users persist in a way. So try to think of users as the base object and notifications as the abundant resource.
The goal here is to get the users notifications so out of this you have to choose whether to duplicate notifications for each user or have one notification for many users. In one case where it's universal notifications (admin panel maybe) and the other is notifications that are personal to the user. If you're doing the latter you don't really need a pivot table and just a notifications table.
User -> hasOne -> Notification
Notification -> belongsTo -> User
This enables you to really customize the notifications per-user than relying on maybe another table for read notifications you can just mark it as "read" in the row.
If however you need universal notifications the structure just implements a third table called a pivot as you know. (I noticed you have the class names pluralized which is not recommended)
User -> Notification_User -> Notification
For ease you also could just soft-delete the notification_user row or notification themselves. You can simply just say ->withTrashed()->limit(x) to get previous notifications.
This really simplifies the work done by the DB and your code. The personal notifications allows you to order by creation/update and deal with read in two ways, soft-deletion and IsRead variables.
Your code becomes as simple as this.
Auth::user()->notifications()
Your User class has the following (assuming standard naming schemes)
public function notifications()
{
//You're free to append other requirements here
return $this->hasMany('App\Notification','id','user_id');
}
The Notification class has the inverse
public function user()
{
//You're free to append other requirements here
return $this->belongsTo('App\User','user_id','id');
}
If for some reason you require to know all unread notifications universally just query the Notification table.
Notification::where('isRead','null')->get();
Alternatively you can lazy load the users for each notification or group by users in this case for whatever purpose you need.
If this helped you to your solution could you mark it as the answer?
You can use the wherePivot and orWherePivot functions provided by laravel for relations. Link
I'm stuck in a problem and I can't find a solution to this, it's annoying me.
I've two tables, one called contacts and the other one called phonebooks and they are linked with a third table called *contacts_phonebooks*, this is a many-to-many relationship summarize below:
contacts: id (pk)
phonebooks: id (pk)
contacts_phonebooks: contactid (fk), phonebooksid (fk)
Pretty simple and clear, and it works.
I'm using Laravel 4 and Eloquent ORM, everythings works fine when I've to fetch it, insert it and delete it but when I need to update a contact I fail miserably. I've a form that has a number of checkboxes that represent all the phonebooks (every checkbox has phonebook[] as name) so when you check one of those the phonebook id will be saved in the *contacts_phonebooks* with the contact id.
The problem is that this is not true! I mean when I run this code:
$contact = Contact::find($id);
$contact->contact_name = Input::get('newCName');
$contact->contact_surname = Input::get('newCSurname');
$contact->contact_email = Input::get('newCEmail');
$contact->contact_phone = Input::get('newCPhone');
$contact->contact_birth = Input::get('newCDate');
$contact->phonebooks()->sync(Input::get('phonebook'));
if($contact->save())
{
return "TEST DONE?";
}
It deletes every row in *contacts_phonebooks* associated with the contact id and save only the new one checked... This is weird I know, I try to explain it better.
I want to update Mr.x and he actually is in "Stackoverflow" phonebook, I want to add him in "Nerd" phonebook so I click on update and I selected "Nerd", the other one is already selected.
When I update him the system deletes the "Stackoverflow" link and save ONLY the "Nerd" phonebook (with the code above) this things driving me crazy because Laravel 4 Doc says that you should use the sync() method in order to update a many-to-many relationship.
I don't how how to solve it, I hope you will understand what's my problem.
Cheers.
The documentation says "The sync method accepts an array of IDs to place on the pivot table. After this operation is complete, only the IDs in the array will be on the intermediate table for the model:"
So what I think you are probably looking for is attach().
$contact->phonebooks()->attach(Input::get('phonebook'));
Then you will have to use detach() to remove him from the other.
As stated in the docs: The sync method accepts an array of IDs to place on the pivot table.
Your pivot table should be named
contact_phonebook
and it specifies that in Laravel's documentation.