Using special union functions inside Laravel Eloquent model - php

I've started developing a website using Laravel, and im pretty much finding everything through the official documentation and answers that I find here. However there is 1 thing that -even though i've found a way to do-, I have a feeling that could be done in another, more optimized way than the one I'm doing it right now. Let me explain.
For my website, I have a table called "players", which has data about some players extracted from a football game. Let's say the structure is like this:
ID (int, primary key, A_I)
GameID (int, unique) //what the game uses
PlayerName
Data (basically many different columns)
Since the purpose of my website is to allow users to do modifications on the game content, I also have another another table that I call "userplayers", which I use for doing modifications on the players that exist on the original table, or for adding new ones. The structure is like the "players" table, however with just one column added, called userID, which is to identify which modification belongs to which user.
ID (int, primary key, A_I)
GameID (int, unique together with userID)
PlayerName
Data (basically many different columns)
UserID (int, unique together with GameID)
If I add an entry on the userplayers table, if that entry has the same GameID as any entry on the players table (and the user that has created it is logged in), then on runtime it overwrites the players' table entry. If the GameID of the new entry doesnt exist on the original players table, then it just gets appended to it.
If the user is not logged in, then I just return the players table.
By using eloquent laravel model I can easily retrieve the players table for when the user is not logged in. However, I can't figure out an efficient way to return the whole DB + the user created content with just using core eloquent model functions.
In the past (without Laravel) I was using this DB query:
SELECT * FROM (SELECT *, NULL FROM players WHERE NOT EXISTS (SELECT * FROM userplayers WHERE players.gameid=userplayers.gameid AND userId=$userId) UNION (SELECT * FROM userplayers WHERE userId=$userId)) AS pl;
And the way I've "found" to do something like this in Laravel is by adding a local scope inside the Players model like this:
public function scopeIncludeCustom($query, $user)
{
return \DB::select('SELECT * FROM (SELECT *, NULL FROM players WHERE NOT EXISTS (SELECT * FROM userplayers WHERE userplayers.gameid=players.gameid AND userId='.$user.') UNION (SELECT * FROM userplayers WHERE userId='.$user.')) AS players_full);
}
However you can understand that this doesn't return the $query as intended by scopes, but just a php array, which im returning back, and I think that's not the correct way to do this. For example, when I'm just searching the players table (without using user created content), it takes a much much shorter time to return results than returning results with the custom content.
Any ideas are hugely appreciated.

So, after some days of researching my issue and possible solutions, I came up with this solution.
So, lets take this step by step.
I had a "Player" model, that fetched data from the "players" table.
I also had a "Userplayer" model, that fetched data from the "userplayers" table.
I had to create a relation between those 2 models. An entry in the "players" table may have many entries related to them in the "userplayers" table. So, in the Player Model I added this:
public function userplayers()
{
return $this->hasMany('App\Userplayer', 'gameid', 'gameid');
}
While in the Userplayer Model, I added this:
public function player()
{
return $this->belongsTo('App\Player', 'gameid', 'gameid');
}
When requesting data, the first step was to remove every row from the "players" table that had the same "GameId" as any row returned from the "userplayers" table, which also had a restriction that the "userId" in every row of this table must be a specific one.
This is the code that does this in my SearchPlayerController:
$orig_players = \App\Player::whereDoesntHave('userplayers', function ($query) use($user)
{
$query->where('userId', $user);
})->get();
At the same time, I need to get every row from the "userplayers" table that has the "userid" I want
$userplayers = \App\Userplayer::where([
['pesid', $search]
])->get();
Now, I just merge the 2 results (I can't use UNION because the data I fetch from the "players" table has one less column).
$players = $orig_players->merge($userplayers)
->sortBy('registeredName')
->take($limit);
And everything works perfectly fine, and a lot faster than before!

Related

Join multiple tables in Yii2 framework

I have 4 tables in my DB:
Table project_ppi:
id (PRIMARY KEY)
Table scopus_author:
id (PRIMARY KEY),
project_ppi, -> linked to id in table project_ppi
author_scopus_id
Table author_subject_area:
author_id (PRIMARY KEY) -> linked to id in table scopus_author
Table project_author_match:
project_ppi (PRIMARY_KEY),
author_scopus_id (PRIMARY KEY),
match_value
What I need to do:
In practice, given a project (table project_ppi) I need to show all the authors (table scopus_author) linked to this project and for each author show his/her area of working (table author_subject_area) but order the result by match_value (table project_author_match).
I'm able to do it in "normal SQL" statements but I'm stuck in doing it in Yii2 framework.
I have a model for each of these tables in my php but I don't really know where to start.
Can anyone give me a hint?
Thanks in advance!
In short
There are already a handful of guides that can help you, and stating that you use a normal SQL database; like MariaDB or MySQL therefore you ought to use ActiveRecord Models. More of details here (https://www.yiiframework.com/doc/guide/2.0/en/db-active-record)
A bit of details
As you should do, you have models for each table, then in each model you add a method that is preferable to be called "getSomething" and this uses the "hasOne" or "hasMany" relation between tables, then in your Yii query you just use the joinWith('relationName') method will making your query. An example:
$query = Post::find()->joinWith('user')->where(['user.id' => 1]);
And this query will get all posts that were written by user whose ID is 1. Assuming a simple database with 2 tables user, post

How to stop auto sorting in laravel pivot table of many to many realtion

I have a author table and a publication table. Both are related to each other in a many to many relation. When I'm inserting the publications the authors of the publications are inserted in the pivot table by the order of authors id. But I need to insert it by the order i'm selecting the authors in the front-end. Whatever the order of the authors in the front end is it is getting ordered by the author's id in the pivot table. How can i stop this automatic ordering
You can't add rows in a specific order into a pivot table, because it doesn't really make sense.
Let's consider an users table:
The first user you enter will have the id 1
The second will be assigned to the id 2
And so on...
So you can enter the users in a specific order and retrieve them by their id.
However, in a standard pivot table, the primary key is composed by two columns, in your case the author_id and publication_id. Nothing new is created here, you just associate the primary key of two existing rows in two differents tables in order to achieve one - and unique - composed primary key.
If i explained well (and i hope so :p), you should understand why saying
But I need to insert it by the order i'm selecting the authors in the front-end.
doesn't really make sense.
But, don't worry, it is still possible to achieve your goal here.
Instead of using a pivot table, you can use a normal table with an id. This way, the order of insertion will be preserved. It will work but that's not very nice.
A better approach would be to add an additional column to the pivot table. A column like position. This column could be incremented for each author you insert. Then, you can order the table by the position column, by simply adding ->orderBy('position') to your relationship or every queries that needs to.
Here is an example to illustrate what i said above:
foreach($authors as $position => $author)
{
$publication->authors()->attach($author, ['position' => $position]);
}
If $authors contains the authors in the order you selected them on the front-end, they will be added accordingly.
If you need to sync instead of attach, that's also possible, it's just a little bit more verbose:
$syncData = $authors->mapWithKeys(function($author, $position){
return [$author->id => ['position' => $position]];
});
$publication->authors()->sync($syncData);
Don't forget that you can add false as a second parameter on the sync method so it'll only add new authors.
After that, just change your authors relationship in your Publication model like this:
public function authors(){
return $this->belongsToMany(Author::class)->orderBy('position');
}
Or everywhere you need to:
$publication->authors()->orderBy('position')->get();
I hope it helps !

Database/PHP design- adding a list of other entity instances to another entity instance?

So my question is very much just a database design question. I'm relatively new to PHP, taking my first database course, and I'm trying to figure out how best to execute my idea.
So I'm building a membership database. Within this database there are "members" and there are "meetings," represented as two separate tables. I'm wondering what might be the best way to add a list of members to a meeting instance, or create a relationship table between the two. For example, would you advise that each member ID (primary key) be added individually (say, via a bunch of text input form fields) when creating a new meeting instance? Or perhaps is there a way to easily have the user upload a CSV or excel file of primary key user id numbers and, from those user number ids, easily create a relationship table?
Hope this is clear- just hoping to get some advice/insight, perhaps I'm not aware of the easiest way... Thanks!
I don't know what are you trying to do in your particular case, but is sounds to me that you should have three tables:
members - you have that one already
meetings - you also have that one already
members_meetings: this one is the table, that will join the two tables. And the required fields in that table should be:
member_id - the id of particular member, points to the id field in your members table
meeting_id - the id of the meeting, this member is attending, points to the id field in the meetings table
Than, if you want to get all members, that are attending meating X, you can just run the following query:
SELECT members.* FROM members_meetings LEFT JOIN members ON members_meetings.member_id = members.id WHERE members_meetings.meeting_id=X

What's the best practice of getting a user's list of article likes from a database?

I'm sort of working on a CMS type structure. I've got to the point where mostly everything is in place except for small things that I keep pondering on like this one.
So let us suppose there is a user table and an articles table. Now, ofcourse if someone 'likes' an article, the user's ID will be stored in a column in the article's table. Something like this 11241,12412,12312. (these are random user IDs)
Now lets say that there's a user's profile page as well and I want to iterate through the user's liked posts and display it there.
Now, I've thought up of 2 ways of doing this.
First method being that the article IDs that the user has liked are stored in the user's table in his row and we get it from that but this seems sort of inefficient. Because if the user has liked a lot of articles, then I could run out of space in the column of the database?
Second method would be to go through every article and see if the user's ID exists in the list of likers saved of the article. However this method would be really slow and a really bad practice, IMO.
So what's the best practice for this?
Create a table that has two columns: user_id and article_id. When a user “likes” an article, insert a row into this table. Then when you want to see which articles user #123 has liked, you can just issue a query like this:
SELECT `article_id` FROM `article_user` WHERE `user_id` = '123';
If you need the article data, it’s easy enough to just join on the article_id column:
SELECT `article`.*
FROM `article`
LEFT JOIN `article_user` ON `article`.`id` = `article_user`.`article_id`
WHERE `article_user`.`user_id` = '123';

In Laravel Eloquent, select "whereIn" from parent table

In my Laravel project (with MySQL database), I have a few models: Time Entries, Tasks, and Projects.
Time Entries belong to Tasks
Tasks belong to Projects
so each table contains a column for the corresponding ID of its parent.
I have an array of Project IDs, and I am trying to select the time entries which, through their tasks, belong to those projects.
In other words, I'd like to be able to do something like this:
$timeEntries = TimeEntry::whereIn('project_id',$projectIds)->get();
But obviously, I get a column not found error, because all I've got in the time entries table is task_id rather than project_id.
Is there a way to select the desired time entries (based on the project IDs I have) in a single Eloquent query? Help much appreciated.
Add the following method in your Project model
public function timeEntries()
{
return $this->hasManyThrough('App\TimeEntry' , 'App\Task');
}
now you can get all time entries of a project like below
$project = Project::find(id);
$project->timeEntries()->get();
So the type of relation you're explaining is a through relation (http://laravel.com/docs/5.1/eloquent-relationships#has-many-through).
Instead of trying to head up the tree head down the tree from projects->tasks->time_entries.
Projects::whereIn($projectIds)->with('time_entries')->get();
The resulting collection will be projects with (should be at least) a field called time_entries in each project that have all the relevant times.

Categories