Laravel Eloquent and 3 table relationship - mysql

I'm looking for some help in working out how to setup the Eloquent relationships for my application.
I have created migrations for three tables.
| users | | items | | user_items |
+-----------+ +-----------+ +------------+
| id | | id | | id |
| username | | name | | user_id |
| item_id |
| qty |
I have setup an unique index on the user_items table, limiting 1 user id to 1 item id, but with a qty column. I want to setup Eloquent so that I have two models, User and Item. Where I could say:
$item = $user->items()->first();
$name = $item->name;
$qty = $item->qty;
The problem is I'm trying to figure out if I need a 3rd model, UserItem or not.

What are you doing here is actually a M:M relationships, and Laravel Eloquent already have support for that out of the box. The user_item table you have is referred to as the pivot table.
Here's how to setup your relationship using Eloquent.
// User class
class User extends Eloquent {
public items() {
return $this->belongsToMany(Item::class)->withPivot('qty');
}
}
class Item extends Eloquent {
public function users() {
return $this->belongsToMany(User::class)->withPivot('qty');
}
}
For this to work, you will need three migrations:
one for the user table
one for the item table
one for the item_user table. In Eloquent, the pivot table consists of the two tables names in alphabetical order. separated with a dash.
following Eloquent conventions, if you set the FK to use _id, Eloquent will not need additional information. So, in your case, the FK in the item_user table should be user_id and item_id respectively.
There is no need for a ItemUser model. You can access the pivot in the relationship by doing:
$user = User::find(1);
$user->items[0]->pivot->qty; // retrieve the quantity

Related

Laravel Relations has problem with retrieve data

I have BelnogstoMany relation in my model which I defined like this
class MailUsers extends Model
{
public function group ()
{
return $this->belongsToMany('App\Models\Group', 'user_groups','user_id','group_id');
}
}
and in the other model, I defined the relationship like this
class Group extends Model
{
public function customer ()
{
return $this->belongsToMany('App\Models\MailUsers', 'user_groups','group_id','user_id');
}
}
when I save data it saves the relation in the user_groups table without problem, and I have this data in it
select * from user_groups;
+----+---------+----------+
| id | user_id | group_id |
+----+---------+----------+
| 1 | 5 | 1 |
| 2 | 16 | 1 |
+----+---------+----------+
my problem is when I am trying to retrieve the data with relation it returns null even it has a relation in the table
$data=MailUsers::with(['group'])->get();
it keeps returning null in the group even the records has relation as I show above
if I try to print the SQL statements with toSql Function it returns this without any join
select * from mail_users;
I have no idea why that is happening , I was working with relation in laravel , and this is the first time that I have just like this problem
Try getting relationship without putting them in []
$data = MailUsers::with('group')->get();
Then, you can get the group as below
$data->pluck('group');

How can I add a group by to a hasMany relationship in laravel 5.3?

I have an elquent model named Conversation.
In that model I define a hasMany relationship on an eloquent model CallParticipant like so:
public function participants(){
return $this->hasMany('App\Models\Purecloud\Analytics\CallParticipant', 'conversationId', 'conversationId');
}
Now, CallParticipants can be system processes, customers, or agents. I need a list of all the CallParticipants that are considered "agents" so I defined another relation like this:
public function agents(){
return $this->hasMany('App\Models\Purecloud\Analytics\CallParticipant', 'conversationId', 'conversationId')
->whereIn('partPurpose', ['agent','user']);
}
Now, an "agent" can be a "participant" multiple times on a "conversation" that is to say there can be multiple rows for the same agent just with a different participantId, like this:
+----------------+---------------+--------------+-------------+
| conversationId | participantId | partUserName | partPurpose |
+----------------+---------------+--------------+-------------+
| 1 | 100 | Alex | agent |
| 1 | 101 | Mary | agent |
| 1 | 102 | Alex | agent | <-- I want to exlcude this
+----------------+---------------+--------------+-------------+
I need to remove these sortof-duplicates so that the relationship only returns one row for each partUserName (one row for Mary, and one row for Alex).
I tried to do this by adding a groupBy like this:
public function agents(){
return $this->hasMany('App\Models\Purecloud\Analytics\CallParticipant', 'conversationId', 'conversationId')
->whereIn('partPurpose', ['agent','user'])
->groupBy('partUserName');
}
But this produces the error:
SQLSTATE[42000]: Syntax error or access violation: 1055 'analytics.callParticipants.participantId' isn't in GROUP BY
I also tried doing it in the with statement like below, but I get the same error:
$conversations = Conversation::with(
[
'agents' => function($query){
$query->groupBy('partUserName');
}
])
->get();
Is there any way I can limit the relationship to only rows with unique partUserName
I think that the only way to do this is by "hacking" the relation. Could you try:
public function agents(){
return $this->hasOne('App\Models\Purecloud\Analytics\CallParticipant', 'conversationId', 'conversationId')
->whereIn('partPurpose', ['agent','user'])
->latest();
}

Arrays in Eloquent ORM (Laravel 5, MySQL)

I have some models:
Expert
id | name | etc
Place
id | name | etc
Review
id | expert_id | place_id | rates..
Rate
id | name | caption |
I need to store rates in Review model (personal, location, internet etc) (or in another model if its not possible). Each Rate haves name and caption
But how should I add values of more than one rate in review? Maybe arrays or something else?
I have this relations
class Place extends Model {
public function reviews() { return $this->hasMany('App\Review'); }
}
class Expert extends Model {
public function reviews() { return $this->hasMany('App\Review'); }
}
I don't know how to make relation Review->Rate, because one review have more than one Rate with value. Where should I store this value? Model Rate contains rate types: only id, name and caption of each rate type.
You have to specify your relationships and relate them together. you can use One-to-One, One-to-Many and Many-to-Many relationships as documented here between Expert, Place, Review and Rate models, to define this relationship, at least four database tables are needed:
experts: id | name | ...
places: id | name | ...
rates: id | name | caption | ...
reviews id | expert_id | place_id | rate_id | ...
For example, you can define many-to-many relationship between expert and place like this:
class Expert extends Model
{
/**
* The places that belong to the user.
*/
public function places()
{
return $this->belongsToMany('App\Place', 'reviews');
}
}
Then you can use attach, detach and sync methods as documented here to insert many-to-many relationships into reviews table:
$expert = App\Expert::find(1);
$expert->places()->attach($placeId, ['rate_id' => $rateId, '...' => ...]);
Short answer would be : using one to many relationship. I wouldnt show the code on how to achieve it, until you try something and show some attempt.

Grails HQL representation of a query

I'm stuck representing in HQL the following MySQL query
SELECT t1.id as user_id, t2.id as follow_id
FROM user t1
INNER JOIN follows t2 ON
(t2.follower_id = <user id> AND t2.followee_id = t1.id) OR
(t2.follower_id = t1.id AND t2.followee_id = <user id>)
WHERE t1.active = true
ORDER BY t1.id
Can you please help me out with the representation?
Thanks!
Edit:
So far I've tried the following HQL query, but it fails to return the proper data:
SELECT t1
FROM User t1
JOIN t1.follows t2
JOIN t2.follower follower
JOIN t2.followee followee
WHERE (follower.id = :usr AND followee.id = t1.id) OR
(follower.id = t1.id AND followee.id = :usr)
AND t1.active = true
ORDER BY t1.id
The idea is that I have two tables:
User table:
+------+--------+-------------+
| id | name | last_name |
+------+--------+-------------+
| 1 | Jhon | Doe |
| 2 | Jane | Doe |
| 3 | Alfred | Hitchcock |
+------+--------+-------------+
Follows table:
+------+-------------+-------------+
| id | follower_id | followee_id |
+------+-------------+-------------+
| 1 | 1 | 3 |
| 2 | 3 | 2 |
| 3 | 2 | 1 |
+------+-------------+-------------+
What I need is, for example, obtain the name and last name of all users who follow or are being followed by, let's say, user id 1.
The SQL query I posted does exactly this, but I cannot seem to replicate the same behaviour in HQL.
2nd Edit:
My simplified domain classes are:
class User {
String name
String lastName
...
static hasMany = [ follows: Follows ]
static mappedBy = [ follows: 'follower' ]
}
And...
class Follows {
User Followee
...
static belongsTo = [follower: User]
}
The important thing to keep in mind when writing HQL queries is that domain class joins (HQL deals with domain classes, not tables) are completely reliant on the domain class associations. To put it differently, the associations actually create the (inner) joins, but HQL allows you to change the join type (LEFT OUTER, RIGHT OUTER, and Cartesian). See my from clause article.
I attempted to derive your domain class associations from your HQL and table descriptions and it just doesn't match up. But here's another way to do it.
Domain model
The domain model is very simple. There's a User class containing a one-to-many association to itself (but it's not a self-join, as you'll see in a moment).
class User {
String name
String lastName
static hasMany = [followees: User]
}
Lets say you have a User instance.
def user = User.get(1)
user.followees contains the Users being followed by user.
Table schema
With such a domain model the tables would look something like this:
user table
+------+--------+-------------+
| id | name | last_name |
+------+--------+-------------+
| 1 | John | Doe |
| 2 | Jane | Doe |
| 3 | Alfred | Hitchcock |
+------+--------+-------------+
user_followees table
+-----------+-------------------+
| user_id | followees_user_id |
+-----------+-------------------+
| 1 | 2 |
| 3 | 1 |
+-----------+-------------------+
The data demonstrated suggests that:
John Doe follows Jane Doe.
Jane Doe is followed by John Doe.
Alfred Hitchcock follows John Doe.
John Doe is followed by Alfred Hitchcock.
This simple one-to-many association can be used to model the two-way relationship that is known as following. The same could be done with two one-to-many associations, which would simplify querying but complicate data maintenance. With this model the two-way following relationships are maintained with a single user.addToFollowee() or user.removeFromFollowee().
Querying
Using the domain model I described, you can query for followees and followers. Given a user...
def followees = user.followees // <-- contains Jane Doe
def followers = User.executeQuery
'SELECT u FROM User as u INNER JOIN u.followees as followee WHERE followee = :user',
[user: user] // <-- contains Alfred Hithchcock
def following = followees + followers
Note that GORM/Hibernate does not support SQL's UNION clause, so you'll need two queries. See my where clause article for more details on GORM's equivalent of the where clause.
Followers
It's also possible to use a where or criteria query to get the followers:
def followers = User.where { followees.id == user.id }.list()
def followers = User.withCriteria { followees { eq 'id', user.id } }
Just for the sake of simplicity and since I was only reading data, I ended up using Groovy SQL which is accepted by Grails and itself accepts the plain SQL query I included in my original post (and doesn't require to match any specific domain model, big plus for me in this scenario)
Here's a link to the JavaDoc, I specifically used the rows(String sql, Map params) method definition to query de DB and return the data I was searching for.
For using groovy.sql.Sql in Grails, I recommend adding it as a bean to the resources.groovy file:
beans = {
groovySql(groovy.sql.Sql, ref('dataSource'))
}
and then inject the same into wherever you wanna use it:
class MyService {
def groovySql
...
def myMethod() {
String query = "SELECT * FROM user WHERE ..."
Map params = [param1: p1]
List<GroovyRowResult> result = groovySql.rows(query, params)
...
}
}
Hope this helps anyone trying to perform plain SQL queries without the hassle of having to convert them to HQL or Criteria.

laravel - alternative to using enum since renaming columns is not supported

The Laravel documentation says
Note: Renaming columns in a table with a enum column is not currently
supported.
What would be a best practice alternative to using an ENUM. For example, I have a users table with a type ENUM column. I might need to modify the table in the future, and being unable to because of the existence of an ENUM column is too restrictive.
What i usually do is: Make a types table.
-----------------------
| id | type |
-----------------------
| 1 | admin |
-----------------------
| 2 | moderator |
-----------------------
In your users table make a field type_id. And create your relation in laravel:
class User extends Model
{
public function type()
{
return $this->hasOne('Type');
}
}
Now you can do:
$users = Users::where('type', '=', 1)->get();
Or:
$users = User::with(['type' => function ($query) {
$query->where('type', '=', 'admin');
}])->get();
And you can also inverse the relationship so you can query by type and load all the users like:
$all = Type::with('users')->where('type', '=', 'admin')->get();