Laravel select with multiple where in statement - php

I have tried various methods to resolve this issue, but none worked for me.
1st method:
$title = Character::find($selected_char->id)->title()->where('title', '=', 'Castle');
$title = $title->where('title', '=', 'City');
$title = $title->get();
2nd method:
$title = Character::find($selected_char->id)->title()->where('title', '=', 'Castle')->where('title', '=', 'City')->get();
3rd method:
$title = DB::select(DB::raw("select * from titles where titles.char_id = 5 and title = 'Castle' and title = 'City'"));
None of the above methods work. If I take only one where clause it works perfectly. Example:
$title = Character::find($selected_char->id)->title()->where('title', '=', 'City')->get();
$title = Character::find($selected_char->id)->title()->where('title', '=', 'Castle')->get();
I even tried to take another column than title, but it doesn't work with a second where function. I want to retreive the rows from titles table where the title is City AND Castle I have used multiple where clauses before in a single select statement and it worked. Not now. Any suggestions? Thanks in advance.

You said:
I want to retreive the rows from titles table where the title is City AND Castle
You may try this:
$rowCOllection = DB::table('titles')
->whereIn('title', array('City', 'Castle'))->get();
Using multiple where:
$rowCOllection = DB::table('titles')
->where('title', 'City')
->where('title', 'Castle')->get();
If you want to add another where clause for titles.char_id then you may use it like:
$rowCOllection = DB::table('titles')
->where('title', 'City')
->where('title', 'Castle')
->where('char_id', 5)->get();
You may chain as much where as you need before you call get() method. You can add the where('char_id', 5) after the whereIn like whereIn(...)->where('char_id', 5) and then call get().
If you have a Title model then you may do the same thing using:
Title::where(...)->where(...)->get();
Same as using DB, only replace the DB::table('titles') with Title, for example:
$rowCOllection = Title::where('title', 'City')
->where('title', 'Castle')
->where('char_id', 5)->get();
What about Character here ?

I don't really know how work your double ->where( in php, but in sql here is the mistake :
When you say where title = 'a' and title = 'b', it's like you say : ok give me something where 0=1 it returns nothing.
You can do :
select * from titles where titles.char_id = 5 and (title = 'Castle' or title = 'City')
Retrieve all data where title equals castle or city
Or
select * from titles where titles.char_id = 5 and title IN ('Castle','City')
Retrieve all data where title equals castle or city using IN
I'm pretty sure you will find a way to do that in PHP too.

Assuming you are using Laravel 4
And Character is your model extended from Eloquent
don't mix FIND and WHERE.
Find is for single usage find AND sorting afterward (so order by, and etc)
So if you want to chain up your query
Character::where()->where()->where()-get() (don't forget the get or else you wont get a result)
this way you respect eloquent's features.
Note your first method with ->title() is flawed because your calling a function that you custom created inside your model - thats why it wouldn't have worked.
Note: WereWolf Alpha's method will also work IF you don't want to use Eloquent because the code that he presented will work but thats Fluent notation...so take your pick.

Related

Laravel, search exact same substring in eloquent query

I have a table like this
Teacher Table
What I am trying to do is to get the row which contains the subjects 1(or any other number like 7,8 etc.)
This is what I have tried in my controller.
public function allTeachers($sub_id) //receiving $sub_id(to be searched)
{
$teachers_all=Teacher::where('subjects','like','%'.','.$sub_id.'%')->latest()->paginate(50);
dd($teachers_all);
}
The problem here is that, I am getting all the rows which contains subjects as '1',e.g. if it is '3,11,22' or '41,5' it gets selected.
But what I am trying to achieve is it should only return where subjects string contains '1' followed by any other number after ',' or '1,2,44,31,23' etc.
I am using laravel, I hope I made the question clear.
The solution to your question would be either to use find_in_set or concat to fill some missing commas and then search for the value:
Teacher::whereRaw('find_in_set("' . $sub_id . '", subjects) <> 0')
->latest()
->paginate(50);
or
Teacher::whereRaw('concat(",", colors, ",") like "%,' . $sub_id . ',%"')
->latest()
->paginate(50);
That being said, #bromeer's comments hold true in any case. MySQL isn't around comma-separated values in fields. Both examples shown above aren't an ideal solution. You should look into relationships a bit more.
I suggest using a many-to-many relationship in your case. For that, create a pivot table called teacher_subject and add the relation to your Teacher model:
public function subjects()
{
return $this->belongsToMany(Subject::class);
}
To find any teachers teaching a specific subject, use whereHas like this:
Teacher::whereHas('subjects', function (Builder $query) use ($sub_id) {
$query->where('id', $sub_id);
})->latest()->paginate(50);

How to get different queries (models) results with one statement? - laravel

Sorry if my title is confusing, not sure how to explain this within a line. Let's say I have a table with some columns and I have this
$model = Document::where('systemName', '=', $systemName)->where('ticketNumber', '=', ($nextTicketNumber))->get(); ticketNumber is unique where as there are quite a few systemNames
The above will get exactly what I want but I want more. I want an another array which will store all the rows under the same systemName. I know I can do this by doing
$allSystemNameModel = Document::where('systemName', '=', $systemName)
But is there a possible way to not having two variables and be easier?
No, you can't get both collections into one variable with one statement, however, you can create an array and store your results there:
$both = [];
$both['model'] = ...
$both['all'] = ...
UPDATE:
To avoid querying the database twice, you can use a first method that laravel provides us with.
$allSystemNameModel = Document::where('systemName', '=', $systemName);
$model = $allSystemNameModel->first(function ($doc) use ($nextTicketNumber) {
return $doc->ticketNumber == $nextTicketNumber;
});
$both['model'] = $model;
$both['all'] = $allSystemNameModel->all();
Note: Be sure to use use when working with php closures since $nextTicketNumber will be undefined otherwise.

Searching API with query string

I am currently building an API, I want to be able to search the API via the query string. So for example http://api.dev/?q=12
I can get the value entered but how do I then use this to search the database?
if ($q = Input::get('q'))
{
return( ModelName::where('id', '=', $q));
}
This doesn't seem to change the search and the same data is just returned.
ID's are unique so it doesn't surprise me you're getting the same result.
For Laravel I find the query builder to be better at handling search queries instead of Eloquent.
E.g.
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
Hope this helps.
To search from the db with Eloquent you can use both this ways:
ModelName::where('id', '=', $q)->get();
or
ModelName::find($q);
In the second example Laravel assumes that your Primary Key is id, so in case you have another field as your primary you have to overwrite it, by setting the property in the model.
class ModelName
{
...your props
private $primaryKey = 'yourPrimaryKey'
...
}

php / database causes performance issue

I am building an online tool for soccerclubs in php. Part of the system is a match module where you can make a video analysis, give grades to players, and more.
Everything is working fine, only problem being the performance of the initial pageload. The reason behind the problem is the database and the way im getting and presenting the data from the database.
My current database looks like this(I removed all unnecessary fields):
http://i.stack.imgur.com/VZnpC.png
When I'm done getting my data from my database I have an object like this:
$match->formations[]->positiongroups[]->positions[]->users[]->statgroups[]->stats[]
The way I'm getting the data takes way to much time( about 12 seconds ) and I'm probably doing it completely wrong. You can see the code below. I use laravel 4 as framework so most code isn't plain php but I think when you read the code you will understand what every line of code does. I do want to notice that an non-laravel solution is fine!
/*
* Get fullmatch info
* Returned value consists of
* $match->formation->positiongroups[]->positions[]->users[]->statgroups[]->stats[]
*/
public static function fullMatch($id){
//Get match of given id with formation and team
$match = Match::where('id', '=', $id)->with('formation', 'team.formation.positions', 'team.category')->first();
if($match->formation){
//Set all positiongroups in $match->formation
$match->formation->positiongroups = Positiongroup::all();
//Get possible positions
foreach(Formation::find($match->formation->id)->positions as $position){
$positions[] = $position->id;
}
//Loop through all positiongroups in $match->formation
foreach($match->formation->positiongroups as $positiongroup){
//Set all positions in positiongroups
$positiongroup->positions = Position::where('positiongroup_id', '=', $positiongroup->id)->whereIn('id', $positions)->get();
foreach($positiongroup->positions as $position){
$position->users = DB::table('formation_position_match_user')
->leftJoin('users', 'user_id', '=', 'users.id')
->where('formation_id', '=', $match->formation->id)
->where('position_id', '=', $position->id)
->where('match_id', '=', $match->id)
->get();
foreach($position->users as $user){
$user->statgroups = Statgroup::where('video', '=', 1)->with('Stats')->get();
$user->stats = DB::table('stat_statdate_user')
->leftJoin('statdates', 'statdate_id', '=', 'statdates.id')
->where('stat_statdate_user.user_id', '=', $user->id)
->groupBy('stat_statdate_user.stat_id')
->orderBy('stat_statdate_user.stat_id')
->get();
}
}
}
}
return $match;
}
If there is more information needed I'm happy to add it to the post.
I haven't seen the queries it produces, but I think you have too many nested foreach loops and you are sending too many queries to database. You should minimize number of queries. You can do it manually or use some library. For example NotORM
edit: Here is example of what you could easily do and improve the performance:
You should focus on getting more data at once, than doing it row by row. For example replace some = in WHERE conditions with IN ();
so instead of sending a lot of queries like
SELECT * FROM positions WHERE position_group_id = x;
SELECT * FROM positions WHERE position_group_id = y;
SELECT * FROM positions WHERE position_group_id = z;
you send only one SELECT to server:
SELECT * FROM positions WHERE position_group_id IN (x, y, z);
You will have to modify your code, but it won't be that difficult... I don't know how your where method works, and if it supports IN statement, but if it does, do something like this:
$position_group_ids = array();
foreach ($match->formation->positiongroups as $positiongroup) {
$position_group_ids[] = $positiongroup->id;
}
$all_positions = Position::where('positiongroup_id', 'IN', $position_group_ids);

How to 'order_by' on second table when using eloquent one-to-many

Of course I can use order_by with columns in my first table but not with columns on second table because results are partial.
If I use 'join' everything works perfect but I need to achieve this in eloquent. Am I doing something wrong?
This is an example:
//with join
$data = DB::table('odt')
->join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
foreach($data as $v){
echo $v->odt.' - '.$v->servicio.'<br>';
}
echo '<br><br>';
//with eloquent
$data = Odt::get();
foreach($data as $odt){
foreach($odt->hdt()->order_by('servicio')->get() as $hdt){
echo $odt->odt.' - '.$hdt->servicio.'<br>';
}
}
In your model you will need to explicitly tell the relation to sort by that field.
So in your odt model add this:
public function hdt() {
return $this->has_many('hdt')->order_by('servicio', 'ASC');
}
This will allow the second table to be sorted when using this relation, and you wont need the order_by line in your Fluent join statement.
I would advise against including the order by in the relational method as codivist suggested. The method you had laid is functionally identical to codivist suggestion.
The difference between the two solutions is that in the first, you are ordering odt ( all results ) by hdt.servicio. In the second you are retrieving odt in it's natural order, then ordering each odt's contained hdt by servico.
The second solution is also much less efficient because you are making one query to pull all odt, then an additional query for each odt to pull it's hdts. Check the profiler. Considering your initial query and that you are only retrieving one column, would something like this work?
HDT::where( 'odt_id', '>', 0 )->order_by( 'servico' )->get('servico');
Now I see it was something simple! I have to do the query on the second table and get contents of the first table using the function odt() witch establish the relation "belongs_to"
//solution
$data = Hdt::order_by('servicio')->get();
foreach($data as $hdt){
echo $hdt->odt->odt.' - '.$hdt->servicio.'<br>';
}
The simple answer is:
$data = Odt::join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
Anything you can do with Fluent you can also do with Eloquent. If your goal is to retrieve hdts with their odts tho, I would recommend the inverse query for improved readability:
$data = Hdt::join('odt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('hdt.servicio as servicio', 'odt.odt as odt'));
Both of these do exactly the same.
To explain why this works:
Whenever you call static methods like Posts::where(...), Eloquent will return a Fluent query for you, exactly the same as DB::table('posts')->where(...). This gives you flexibility to build whichever queries you like. Here's an example:
// Retrieves last 10 posts by Johnny within Laravel category
$posts = Posts::join('authors', 'authors.id', '=', 'posts.author_id')
->join('categories', 'categories.id', '=', 'posts.category_id')
->where('authors.username', '=', 'johnny')
->where('categories.name', '=', 'laravel')
->order_by('posts.created_at', 'DESC')
->take(10)
->get('posts.*');

Categories