Laravel Eloquent: How to select from multiple tables - php

I'm using Laravel and the Eloquent ORM that it provides but I'm struggling to select the data I need. I have 3 Models
House
Occupants
Job
A house can have multiple occupants and I can easily get these using the following.
$house= House::find($house_id);
$occupants = $house->occupants()->where('active', 1)->get();
This works nicely but I also want to select the job of each occupant. I've got this as a one to one relationship but the jobs are in a seperate table.
Is there a way to also select the related job for each occupant from the jobs table efficiently? I'm guessing it would be something like this
$occupants_and_jobs = $house->occupants()->where('active', 1)->job()->get();

You could just try what you are suggesting and see what happens.
You can also do some eager loading of the relationships
$house = House::with(["occupants","occupants.job"])->find($house_id);
foreach ($house->occupants as $occupant) {
print_r($occupant->job);
}
As #AlexeyMezenin if you need to constrain the relationship then you need to (as the docs suggest under Constraining Eager Loads) do:
$house = House::with(["occupants" => function ($query) {
$query->where("active","=",1);
},"occupants.job"])->find($house_id);
foreach ($house->occupants as $occupant) {
print_r($occupant->job);
}
Now the fine-print: Laravel will include "with" relationships in the order it finds them and also include all intermediate relationships of the nesting, e.g. ::with("occupants.job") implies ::with(["occupants","occupants.job"]) however if you already have set a previous relationship then it is maintained (which is how this works). occupants will not be overwritten when occupants.job is set.

This query will load all occupants with active = 1 and their jobs:
House::with(['occupants' => function($q) {
$q->where('active', 1);
}, 'occupants.job'])->find($house_id);

Complete way to to search data from different tables in laravel:
Route:
Route::get('/systems/search', 'SearchController#search')->name('project-search');
Try this way in your searchController:
public function search(Request $request)
{
$data = DB::table('projects as p')->select('p.*','u.first_name', 'u.last_name')
->join('users as u','p.user_id','=','u.id');
if( $request->input('search')){
$data = $data->where('p.title', 'LIKE', "%" . $request->search . "%")
->orWhere('p.description', 'LIKE', "%" . $request->search . "%")
->orWhere('u.first_name', 'LIKE', "%" . $request->search . "%")
->orderBy('p.created_at', 'desc');
}
$data = $data->paginate(16);
return view('systems.search', compact('data'));
}
And receive those data in your blade like this way:
<div class="col-md-8">
<h3>List of Results</h3>
<table class="table table-striped">
<tr>
<th>title</th>
<th>description</th>
<th>creator</th>
<th>category</th>
</tr>
#foreach($data as $search)
<tr>
<td>{{ $search->title }}</td>
<td>{{ $search->description }}</td>
<td>{{ $search->first_name }}</td>
</tr>
#endforeach
</table>
{{ $data->appends(request()->except('page'))->links() }}
</div>
search box input field:
<div class="input-group input_search" style="width: 100%">
<input style="border-radius: 20px" type="text" class="form-control search"
placeholder="Search" aria-describedby="basic-addon2" name="search" id="search"
value="{{Request::get('title')}}">

Related

How do i select items from a table(id) to display matched id from another table?

I have 2 tables in my db, 1 for Collabs and 1 for Projects
I want when I view a project, to display the collabs based on that project (if i press view on a project for example project with id = 10, to display all Collabs for the project that is id 10).
For Collabs table I have id_project that is wanted to be in relationship with id from Project table, so when I insert a new collab in my Collabs table it takes the id from the project I inserted.
For now, this is how I display the collabs, and i display them all for all projects but I don't want that.
#forelse ($istoric as $istProj)
<div class="mb-3">
<table class='table'>
<tr class="table-row-heads">
<th>Id Proiect</th>
<th>Tip actiune </th>
<th>Colaborator </th>
<th>Suma </th>
<th>Data </th>
</tr>
<tr class="table-row-data">
<td>{{ $istProj->id_proiect }}</td>
<td>{{ $istProj->action_type }}</td>
<td>{{ $istProj->colaborator_id }}</td>
<td>{{ $istProj->suma }}</td>
<td>{{ $istProj->data }}</td>
</tr>
</table>
</div>
#empty
<div class="card-body">
<h2>Nu au fost gasite inregistrari</h2>
</div>
#endforelse
You should really consider reading and watching more videos on how relationships and eloquent works, I hope this below is a good reference for you to get started, please read carefully, and sorry I couldn't translate back to romanian, and to avoid any mistakes, I kept my code in english.
Caloboratori = Colaborators
Istoric Proiecte = Project History
id || auto_increment
project_id || bigInteger()
colaborator_id || bigInteger()
Proiecte = Project
id || auto_increment
Project Model
/* To load the history, we will be using hasMany relationship, because for each
project, we have lots of history, please read more on one-to-many relationships here
https://laravel.com/docs/9.x/eloquent-relationships#one-to-many
Istoric Proiecte = Project History
id || auto_increment
project_id || bigInteger()
colaborator_id || bigInteger()
*/
public function histories() {
return $this->hasMany(ProjectHistory::class);
}
Project History Model
//We will reverse the one-to-many relationship, with belongsTo here. | example: project_id
public function project() {
return $this->belongsTo(Project::class);
}
//We will reverse the one-to-many relationship, with belongsTo here. | example: colaborator_id
public function colaborator() {
return $this->belongsTo(Colaborator::class);
}
Projects Controller:
// Show a list of all projects
public function index() {
//Get all projects
$projects = Project::all();
//Load all of the project relationships that we will be using
$projects->load('histories.colaborator');
return view('projects.index', compact('projects'));
}
// Show a single project
public function show(Project $project) {
//Load all of the project relationships that we will be using
$project->load('histories.colaborator');
//Assign the loaded project history
$histories = $project->histories;
return view('projects.show', compact('project', 'histories'));
}
projects.index Blade: in this blade, you can forloop thru all of your projects model, and assign them as $project, since we loaded the relationships earlier from the controller.
You can easily access the relationships using $project->histories then assign each history model to $history.
Then you can go one step inside of the history relationship and call the inner relationship of colaborator with $history->colaborator
#foreach ($projects as $project)
<p>Project id: {{ $project->id }}
<p>Project name: {{ $project->name }}
<h1>Project History list</h1>
#foreach ($project->histories as $history)
<ul>
<li>ID: {{$history->id}}</li>
<li>Name: {{$history->name}}</li>
<li>Colaborator Name: {{$history->colaborator->name}}</li>
</ul>
#endforeach
#endforeach
projects.show Blade: in this blade, we have a single project, and you can forloop thru all of your history models, since we loaded the relationships from the controller.
We assigned the histories collection as $histories then assign each history model to $history
Then you can go one step inside the history relationship and call the inner relationship of colaborator with $history->colaborator
<p>Project name: {{ $project->name }}
<h1>Project History list</h1>
#foreach ($histories as $history)
<ul>
<li>ID: {{$history->id}}</li>
<li>Name: {{$history->name}}</li>
<li>Colaborator Name: {{$history->colaborator->name}}</li>
</ul>
#endforeach
If you use model Collab, and within you have project relation , than you can use
Collab::query()
->with('project', function ($q) use ($id) {
return $q->where('id', $id);
})
->get();
Or you can use query builder as well
DB::table('collabs')
->select('collabs.*')
->join('projects', 'projects.id', '=', 'collabs.project_id')
->where('projects.id', $id)
->get();
Just adjust it according what you really need.

Eloquent collide column names

I have the following tables:
users: id, name, ...
companies: id, name, ...
I have a scope with a LIKE operator, And I use it to search in both users.name and companies.name as follows:
public function scopeSearch(Builder $query, string $term = null)
{
$query->join('companies', 'companies.id', '=', 'users.company_id');
$term = $term.'%';
$query->where('users.name', 'like', $term)
->orWhere('companies.name', 'like', $term);
}
Action:
$users = User::query()
->search($request->input('q'))
->with('company')
->paginate();
// ...
Since name was used in both tables, Laravel can't distinguish between them, so it ends up filling the users.name with a companies.name:
The view looks like this:
#foreach($users as $user)
// ...
<th scope="row">{{ $user->id }}</th>
<td>{{ $user->name }}</td> // wrong: this gives the company.name
<td>{{ $user->company->name }}</td>
<td>{{ $user->email }}</td>
// ...
#endforeach
One solution to solve this is by using select and some aliases, but that would be a nasty solution.
Any good solution?
Update 1: I don't want to use whereHas since it's slower than joining when it comes to using the LIKE operator.
Update 2: another solution is to alias the companies.name and the users.name as follows:
public function scopeSearch(Builder $query, string $term = null)
{
$query->join('companies', 'companies.id', '=', 'users.company_id')
->select(['*', 'companies.name as company_name', 'users.name as user_name']);
}
<td>{{ $user->user_name }}</td>
<td>{{ $user->company_name }}</td>
This works fine, but if (for any reason) I want to get rid of the searching capability, I also need to modify the view, so a dependency has been introduced here.
I would probably rewrite the search-scope to search for the company name through relations and using whereHas(), instead of joining it in. That way you never have ambiguous column names when using that scope.
It would generate a different query, but you would end up with the same result.
public function scopeSearch(Builder $query, string $term = null)
{
$term = $term.'%';
return $query->where('name', 'like', $term)
->orWhereHas('company', function($query) use ($term) {
return $query->where('name', 'like', $term);
});
}
After doing some experiments, I found that using the whereIn clause is faster and more convenient than using join/whereHas:
$query->where('name', 'like', $term)
->orWhereIn('company_id', function($query) use ($term) {
$query->select('id')->from('companies')->where('name', 'like', $term);
});
The action remains the same:
<td>{{ $user->name }}</td>
<td>{{ $user->company->name }}</td>
And as I said it's much faster than joining and whereHas.
Here is my benchmark:
600ms: whereHas.
500ms: join.
130ms: whereIn.

How to get all records in laravel Models

I'm not using eloquent, my models are like this.
class drivers extends Model
{
}
I want to display records of all drivers ( one record in each row )
My driver table has field (driver_id,name,tyre_id)
My tyre table has field (id, title)
My bank table has field (id, driver_id, bank)
I want my record to be like this...
Driver Id, Name, Bank Details, Tyre Title
100000111, Ayer, Available, GO
.. so on
For bank details if driver_id has a record in bank table, it should display available otherwise N/A.
$drivers= Drivers::get();
$myarray = ();
foreach ($drivers as $d){
$bank = Bank::where('driver_id',$d->driver_id)->first();
$tyre = Tyre::where('id',$d->tyre_id)->first();
$myarray[] = $d->driver_id;
$myarray[] = $d->name;
$myarray[] = isset($bank) ? "available" ; '';
$myarray[] = $tyre->title;
}
This is what i have tried, I'm to new to laravel, how can i achieve this in laravel or using query like DB Table?
Laravel offers two very useful tools for performing database operations eloquent and query builder. It is advisable to work as much as possible with eloquent and their relationships as it facilitates much of the common operations that we normally need to perform.
Now, if you want to make more complex queries you can use query builder, also, an example of your case using query builder would be something like this:
In your controller you make the query and pass the data to view:
$data = DB::table('driver')
->leftJoin('bank', 'bank.driver_id','=', 'driver.driver_id')
->join('tyre', 'tyre.id','=', 'bank.tyre_id')
->select('driver.driver_id as id',
'driver.name',
'bank.id as bank_id',
'tyre.title')
->groupBy('driver.driver_id')
->get()
And in your view you can use a foreach loop to display the data (and you can use a conditional to display the bank field):
<table>
<thead>
<tr>
<th>Driver ID</th>
<th>Name</th>
<th>Bank Detail</th>
<th>Tyre Title</th>
</tr>
</thead>
<tbody>
#foreach($data as $item)
<tr>
<td>{{ $item->id }}</td>
<td>{{ $item->name }}</td>
<td>{{ isset($item->bank_id) ? "availible":"N/A" }}</td>
<td>{{ $item->title }}</td>
</tr>
#endforeach
</tbody>
</table>
likewise I recommend you read the documentation of eloquent and try to use it.
https://laravel.com/docs/5.5/queries
https://laravel.com/docs/5.5/eloquent
The Good way to Solve this is laravel relations
here the link
laravel documentation
Select your driver table as base table and use relations to get the other table fields;
array_push() function to push values to array
Another way is using DB Facade with joins: Like this:
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();

How to use pagination along with laravel eloquent find method in laravel 5.4

I am trying to implement pagination in laravel and got following error
Undefined property: Illuminate\Pagination\LengthAwarePaginator::$name
Here is my controller function
public function showTags($id)
{
$tag = Tag::find($id)->paginate(5);
// when lazy loading
$tag->load(['posts' => function ($q) {
$q->orderBy('id', 'desc');
}]);
return view('blog.showtags')->withTag($tag);
}
Here is the Tag Model
class Tag extends Model
{
public function posts()
{
return $this->belongsToMany('App\Post');
}
}
The Tag and Post model has belongsToMany Relationship so there are many posts under the specific tag and my aim is to iterate all posts under the specific tags descending order of post and also to implement pagination in that page.
Here is the code for showtags view
<table class="table">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Tags</th>
</tr>
</thead>
<tbody>
<?php $count = 1; ?>
#foreach($tag->posts as $post)
<tr>
<th>{{ $count++ }}</th>
<th>{{ $post->title }}</th>
<th>#foreach($post->tags as $tag)
<span class="label label-default">{{ $tag->name }}</span>
#endforeach
</th>
</tr>
#endforeach
</tbody>
</table>
//Here is the code i used for pagination in view
<div class="text-center">
{!! $tag->posts->links() !!}
</div>
If anybody know how to do this please respond. Thanks in advance.
I solve the problem by using a simple trick. My aim was to paginate all posts under the same tags just like you guys can see in StackOverflow.
The modified controller function is
public function showTags($id)
{
$tag = Tag::find($id);
// when lazy loading
$tag->load(['posts' => function ($q) {
$q->orderBy('id', 'desc')->paginate(10);
}]);
return view('blog.showtags')->withTag($tag);
}
As you guys see that I move the paginate() function from find to load function which I use before for sorting post by descending order.
Now in view instead of using traditional method {!! $tag->links() !!} for making link of pagination
I use {!! $tag->paginate(10) !!}
With this line $tag = Tag::find($id)->paginate(5); You should get only one tag(or null if tag with your id dose not exist), and after that you want paginate it. If you want paginate your tags get all tags and after that paginate it Tag::paginate(5)

Pagination doesn't work after doing a search Laravel

Hi when I'm doing a search for a order, then I can't get my orders paginated (5 orders per page)..
I have also recorded myself to show you the problem that I currently have:
link: https://www.youtube.com/watch?v=sffTI6adS7A&feature=youtu.be
This is the code that I'm using:
OrdersController.php:
public function post_search()
{
$keyword=Input::get('keyword');
if(empty($keyword))
return Redirect::route('user.orders.index')->with('error_search', 'Er was geen zoekterm ingevuld, probeer het nog eens.');
//$orders = Order::with('client')->get();
//$orders= new stdClass;
//$orders->foo=Order::search($keyword)->paginate(5);
$orders=Order::search($keyword);
$msg="";
if (!count($orders)) {
$msg="There were no orders found.";
}else{
$msg="There were orders found";
}
$orders = Paginator::make($orders, count($orders), 5);
foreach( $orders as &$order){
//we initialize ordertask
$order->ordertask = Ordertask::where('id_order', '=', $order->id)->get();
}
return View::make('user.orders.index', compact('orders', 'msg'));
}
Order.php:
public static function search($keyword){
DB::connection()->getPdo()->quote($keyword);
$result = DB::Select(DB::raw('SELECT orders.*, tasks_x.task_all
FROM orders LEFT JOIN (SELECT GROUP_CONCAT(tasks.task_name SEPARATOR ",")
AS task_all, ordertasks.id_order
FROM tasks JOIN ordertasks
on ordertasks.id_task = tasks.id GROUP BY ordertasks.id_order) as tasks_x
ON tasks_x.id_order = orders.id WHERE orders.order_name
LIKE "%'.$keyword.'%" OR tasks_x.task_all LIKE "%'.$keyword.'%"'));
return $result;
}
What have I tried:
I have changed the form action to GET and POST, but that didn't work.
Can someone maybe help me, please?
You can't paginate a raw query using laravel paginator. You can only paginate queries made with the query builder or an Eloquent model.
You would have to manually paginate the query (using LIMIT in your query), and then use Paginator::Make() to show the paginator view. Also, you would have to retrieve the number of items with a different query, as using count() over the results array would give you the number of results in that page (Generally, the page size, except for last page)
Other option would be to change from that raw query to a query made using the query builder, but laravel doesn't recommends this as you're using "Group By" and the official docs says:
Note: Currently, pagination operations that use a groupBy statement cannot be executed efficiently by Laravel. If you need to use a groupBy with a paginated result set, it is recommended that you query the database manually and use Paginator::make.
Official Doc
This is what I do in every pagination search:
Controller
$query = new Product();
//dynamic search term
if (Input::has('code')){
$term = Input::get('code');
$term = trim($term);
$term = str_replace(" ", "|", $term);
$query = $query->whereRaw("itemcode regexp '".$term."'");
if (Input::has('description')){
$term = Input::get('description');
$term = trim($term);
$term = str_replace(" ", "|", $term);
$query = $query->whereRaw("itemcode regexp '".$term."'");
}
//if there are more search terms, just copy statements above
$products = $query->paginate(50); //get 50 data per page
$products->setPath(''); //in case the page generate '/?' link.
$pagination = $products->appends(array('code' => Input::get('code'),
'description' => Input::get('description')
//add more element if you have more search terms
));
//this will append the url with your search terms
return redirect('product')->with('products', $products)->with('pagination', $pagination);
View
<div class="panel panel-default">
<div class="panel-heading">
<h6 class="panel-title"><i class="icon-paragraph-justify"></i> Striped & bordered datatable</h6>
</div>
<div class="datatable">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>#</th>
<th>Item Code</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#foreach($products as $key)
<tr>
<td>{{ $key->id}}</td>
<td>{{ $key->itemcode }}</td>
<td>{{ $key->description }}</td>
</tr>
#endforeach
</tbody>
</table>
</div>
</div>
{!! $products->render() !!}

Categories