retrieve unread messages as model method eloquent - php

I have a simple messaging system, the works like this,
1 user can own many conversations
1 user can be in receipt of many conversations
1 conversation has many messages
The table structure looks like this,
Coversation.
ID |
title |
created_by (user) |
recipient (user) |
created_at |
updated_at |
Messages.
ID | content | conversation_id | sent_by | read_at | created_at | updated_at
What I am wanting to achieve is to get a count (or collection) all the unread users as relationship/model function, i.e.
$user->unreadMessages();
Is this possible? I am struggling to work out how to do it, as I cant just query the messages, I need only the messages for conversations that I the user is involved in but they have not sent.
Hope some can help. I am utterly confused, maybe database needs some redesigning to aid read receipts?

As mentioned in the comments use the hasManyThrough which provides access to distant relations via an intermediate relation.
Define relationships as follow:
User Model
/**
* Get all of the UnReadMessages of the user.
*/
public function unreadMessages()
{
return $this->hasManyThrough(
Message::class,
Conversation::class,
'created_by',
'conversation_id')
->whereNull('read_at');
}

Related

Laravel's pivot table + Pivot table in general

What are laravel pivot tables and pivot tables in general? What is this all about?
Recently I made research about Pivot table. I thought I know them and What they are but then I probably was wrong about that.
I have always thought that a pivot table is just a table that is between two tables (Relation many to many)
But then I started this research and It happened to be not that, but something like different architecture of normal table, where rows are columns. It's changed.
But then Laravel's got pivot tables too. Started reading the documentation and doing research.Maybe I read wrong, but it looks like just pivot table in laravel - table in between two tables, many-to-many.
Searching elsewhere but can't find proper information about it.
Okay, so be it. Laravel's pivot just many to many!
Then I started project and Today I went to the point that making this in-between table as pivot drived me to an Issue, I had a problem with that... minutes and hours... couldn't fix that.
Model was class Room_Student extends Pivot
And what was the fix? Just changing it to class Room_Student extends Model.
I don't think I understand pivot tables anymore and are they different types of pivots? Laravel's pivots are different?
So my question is, what pivot tables really are? + Laravel's pivot tables. Are they different? What is this about?
Please help me understand this.
When learning, focus only the pivot tables concept in Laravel (or eloquent). When I was learning I did not care about the general meaning of pivot table. I focused only on facts in the documentation (https://laravel.com/docs/5.5/eloquent-relationships#many-to-many)
many-to-many relationships require an additional table.And we can insert other useful data to this table as well. And can be used as a model in the system.
Example :
User and Roles many-to-many relationship = User_roles
Because of Pivot tables, you can retrieve intermediate table data as a model (like other models in the system).
Example:
//get user by id
$user = App\User::find(1);
//get roles of this user
foreach ($user->roles as $role) {
//pivot attribute returns a model which represent user_role table
echo $role->pivot->created_at;
}
NOTE: you can create a class by extending pivot. But you have to implement the correct relationships to make it work. Your code should look somewhat similar to below code.
class Student extends Model
{
/**
* The users that belong to the role.
*/
public function Rooms()
{
return $this->belongsToMany('App\Room')->using('App\Room_Student');
}
}
class Room extends Model
{
/**
* The users that belong to the role.
*/
public function Students()
{
return $this->belongsToMany('App\Student')->using('App\Room_Student');
}
}
class Room_Student extends Pivot
{
//
}
I hope this helps.
Simply put is a pivot table a table that joins two tables together
say you have a table users
USERS:
user_id, user_name
say you have a table games
GAMES
game_id, game_name
a user can play many games. games have many users playing them.
To link them you make a third table
GAMES_TO_USERS
game_id, user_id
with this table you can request which games a user plays, and which users play which game.
this table GAMES_TO_USERS is in this case the pivot table.
If you know about many-to-many relationships this is common, to handle many-to-many relationships we use intermediate (pivot) table to store relationships of two tables.
Ex: consider about “education” and “person” tables which are listed below
table: person
|------|-------|-----|
| id | name | age |
|------|-------|-----|
| 1 | Bob | 30 |
| 2 | John | 34 |
| 3 | Marta | 28 |
|------|-------|-----|
table: education
|------|-------|
| id | level |
|------|-------|
| 1 | BSc |
| 2 | MSc |
| 3 | PhD |
|------|-------|
Think that Bob has BSc, MSc and John has BSc, MSc, PhD and Marta has BSc, now this is consider as many-to-many relationship and to sort this relationship you need to have intermediate table such as,
table: person_education
|------------|--------------|
| person_id | education_id |
|------------|--------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 1 |
| 2 | 3 |
| 3 | 1 |
|------------|--------------|
This table mainly stores the primary keys (IDs) of each relationship. This is the basic idea behind the pivot table and when you use larval there are some best practises such as,
Name of the pivot table should consist of singular names of both
tables, separated by undescore symbole and these names should be
arranged in alphabetical order
Laravel Ex:
Class Person extends Model {
public function education ()
{
return $this->belongsToMany('App\Education', 'person_education');
}
}
Moreover, you can specify the actual field names of that pivot table, if they are different than default person_id and education _id. Then just add two more parameters – first, the current model field, and then the field of the model being joined
public function education() {
return $this->belongsToMany('App\Products', 'products_shops', 'shops_id', 'products_id');
}
Keep this in mind
Pivot table — is a table used for connecting relationships between two tables.
Laravel part — laravel provides many-to-many relationship where you can use pivot table, and it is very useful for many cases.
Example:
databases: users, post_user, posts
User.php (Model)
class User extends Model{
public function posts(){
return $this->belongsToMany('Post');
}
}
Now, to access all posts of authenticated user: (view)
#foreach(auth()->user()->posts as $post)
<li>{{ $post->name }}</li>
#endforeach
Relationship happened:
Remember we have post_user table which is the pivot table we used. If we have:
user_id of 1 and we expect that it is the logged in user and post_id of 1, 2, 3, 4, all this posts will be printed out like so:
|------------|--------------|
| post_id | user_id |
|------------|--------------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
|------------|--------------|
Output:
PostName1
PostName2
PostName3
PostName4

Mark is_read in Notification system with Laravel 5

I have a site where users can post things like statuses, pictures, etc. I'm wan't to create a notification system similar to Facebook . Something that alerts the user of their friends recent activity. My problem is i'm not sure how I should structure my table. I'm following some advice from an older tutorial Tutorial for the db scheme, and doing something like this
id | user_id | subject | body | object_id | object_type | is_read | sent_at | created_at | updated_at
I know what to do for the majority of the code, or at least have a good idea, my problem and question is for the is_read column, should i create a separate table to store that information, if not then how can I mark a notification as is_read with the current setup.
I guess you want to have a "is read" for EVERY friend/user, this was a bit unclear. If this is the case your best bet (in my opinion) would be to create a new table, as you said, containing the post_id, and user_id. All you then have to do is to check is such a relationship exists, and if so, show a notification.

How to return multiple relationships with Laravel Eloquent?

I have a table called users. Each of these users has different things:
country
device
computer
category
I have created a table for each of these above 'things'. Similar the following:
1 | United States
2 | United Kingdom
3 | Australia
4 | Canada
5 | Italy
etc...
I'm storing these values in the users table as follows:
ID | Country | Device | Computer | Category |
---|---------|--------|----------|----------|
1 | 3 | 2 | 6 | 2 |
2 | 4 | 3 | 9 | 1 |
etc...
Now each of the above numbers are associated with the corresponding table's ID.
What I want is do an Eloquent Query and search for all the users and 'replacing' their corresponding values from their helper table.
I was thinking about doing a hasOne() Eloquent relationship for each of those things in the users table, but then I'm not sure how to get/call them at once.
Anyone can help me tacke this issue?
$model = Model::with('relatedModel', 'relatedModelTwo')->get();
So in your case, it can be something like this. If you only want one relations returned with the user, remove the others.
$user = User::with('country', 'Device', 'Computer', 'Category')->get();
When you dd($user), you should see the related models in the relations array attribute.
To access the relations' properties from there, it is simply
$user->device->deviceAttribute
Edit For Comment:
Eloquent will assume the foreign key uses the Model name. So if your user table has the id column, it assumes there will be a column on device table called user_id. If you did this, then you are set and nothing should have to be done.
If you named your column to relate the models something else, you will need to pass that through as a second argument in the relation method.
public function device()
{
return $this->hasOne('App\Device', 'foreign_key_here',);
}
Inversely, if you wanted to get the user the device belongs to, Eloquent will attempt to match the user_id column on the device table to the id column on the user table. As before, if you used a different column to related them, declare the foreign key as the second argument.
public function user()
{
return $this->belongsTo('App\User', 'foreign_key_here',);
}
Laravel Doc One-To-One Relation
You're looking for this: $books = App\Book::with('author', 'publisher')->get();
Check out the documentation for eager loading multiple relationships.

How do I design a flexible MySQL notification schema that references multiple tables?

I decided to work on a notification system with inspiration from another StackOverflow post that suggested I break up the notification from the body of the notification. As such, I have the following schema:
notifications
id | user_id | notification_object_id | is_read
notification_objects
id | message | type
I have it set up this way so I can create a notification object and send this out to multiple users in the case the notification object applies to multiple people. The type is there to condense messages into one notification by type, sort of the way Facebook says, "John Doe, John Smith, and 27 other users commented..." rather than giving you 29 separate notifications.
(The naming schema of the table and column are for CakePHP 3's 'contains')
What I need help on is a way to tie in the different users or objects that would be playing a role in the requests. A user might have commented, so I'll probably need the user_id of the user who messaged and need to store that somewhere.
There are other cases besides users; one might want to know the id of the comment such that he can click the notification and go straight to comment/81. I have many use cases where I want to store the different types of ids that are foreign keys to other tables (users, comments, articles, etc.), some of which might need two foreign keys (say you want to mention "Chris has commented on your article 'How to beg StackOverflow for help'") and have the user_id for Chris and the article id for the article both linked in the notification.
Each different type of notification could have different values and tables it needs to pull information from. What's a good place to start on creating this schema?
I'm going to try and answer this question from an OO perspective. When speaking in classes, not in tables, you'd probably start with something like an AbstractNotificationObject class with the basic properties (date, time, maybe a message) and several specialization classes like NewCommentNotificationObject with additional properties (comment, commenting user) or a NewFriendNotificationObject. Going down to the database schema, you could then apply one of the typical ORM patterns.
Concrete table inheritance (just for completeness)
Using concrete table inhericance would probably not be a good idea, because it would boil down to a separate table for each notification type (i.e. a comment_notification_objects table and maybe many more). This would not scale well, and you would not be able to reference them from the notification table using a foreign key. Also, you would not be able to select a set of notifications of different types with a single query (without something like a UNION).
Single table inheritance
Single table inheritance would use only a single table with a type discriminator column. This single table (let's call it notification_objects) would need to contain a type column that describes of what type of notification the record actually is (you actually already have that in your schema). You'll also need columns for all properties that special notification types may have (for instance, a user_id and a comment_id column).
+--------------+
| notification |
+--------------+
| id |
+--| object_id |
| +--------------+
|
| +-----------------------+
| | notification_objects |
| +-----------------------+
+->| id PK |
| date |
| type |
| user_id FK |--...
| comment_id FK |--...
| friend_id FK |--...
| [more fields to come] |
+-----------------------+
Pros:
Load all notifications with a single query
Keep referential integrity with foreign keys
Cons:
Scales badly (you'll need to add new columns for each notification type)
Table will be sparsely populated (many columns will be NULL)
I would recommend this schema when you have a set of notification types of moderate size that does not regularly change.
Class table inheritance
Class table inheritance is somewhere in between; here you would create a central notification_objects table and separate tables for each notification type (for example an additional comment_notification_object table with an id column (which in turn is a foreign key to the notification_object table and a user_id and column_id:
+--------------+
| notification |
+--------------+
| id |
+--| object_id |
| +--------------+
|
| +----------------------+ +------------------------------+
| | notification_objects | | comment_notification_objects |
| +----------------------+ +------------------------------+
+->| id PK |<--+--| id PK/FK |
| date | | | comment_id FK |--...
| type | | | user_id FK |--...
+----------------------+ | +------------------------------+
|
| +--------------------------------+
| | newfriend_notification_objects |
| +--------------------------------+
+--| id PK/FK |
| friend_id FK |--...
+--------------------------------+
[more TABLES to come...]
Class table inheritance would also allow you to query all notifications with a single select query. Depending on how much of the data you need, you'll need to add JOINs to the respective specialized tables.
Pros:
Scales well/Add new types without modifying existing structure
Keep referential integrity with foreign keys
Cons:
When you need more than the minimal set of fields, you'll need to JOIN the specialized tables (possible performance impact)
I would recommend this schema when you have a lot of different notification types or extend your object model often and need to change/add new notification types on a regular basis.
Outside the relational box
Your question asked explicitly about relational schema, so I've focused on that. When thinking outside this box, alternate solutions might include:
Schemaless NoSQL databases
Messaging systems that follow a publish/subscribe architecture

Save array in one field of database or save each value in separated field?

I work on a message system. in this message system admin can send messages to single user, user group or all users and users just can see messages.
know i want to find unread messages in user panel.
i designed two database table like this :
1- msg_messages :
| id | title | messages | date | status |
2- msg_control :
| id | message_id | from | toUser | toGroup | status |
Know i think about this two methods of saving users who saw the messages.
Method 1 : create a string with readers id Like this : 1,5,9,12,... and check users id with this string by Strpos php function or in_array function.
Method 2 : create a new table like below table and save readers id in that :
| id | message_id | readers_id | date |
which one of this two method is better ?
A new way to find unread messages ...
after think more about this problem i decided to use a new method by combining method 1 and method 2. actually i added a new column to user table and named it read_msg . system will save read message_id in this filed in a string like this 1,5,9,12,98,125,... for each user (message_id in this filed refer to msg_control table ) and when we want to find unread messages just need to compare msg_control ids with this filed.
we use strpos to compare read_msg and message_id because its faster than is_array (reference).
I once implemented a small messaging system and structured it as follows (however it might not be perfect and there are multiple possibilities):
Tab Conversation
ID | Conv Title | ... master data
Participants
UserId | ConversationID | lastRead
MessageHeaders
ConversationID | messageID | ... use your "inbox"-schema here. You can remove the toUser/toGroup etc. fields as this is covered using the participants table.
Using the timestamps of a message and the "last-read"-timestamp of a user for a conversation you can determine which messages are new/unread and which are not.
Using constructs like in your first suggestion can become very inefficient. Storing data atomar is considered good practice in my opinion. This means no multiple values in a single field.

Categories