Laravel - Math computations in blades - php

Is it possible to add two integers from the database inside a blade?
To give a scenario, I have a controller that compacts a collection of orders table.
$solditems = DB::table('orders')
->where('status', 'served')
->orderBy('id')
->get();
return view('salesreports.sellingitems.index', compact('solditems'));
And I used those like this in my blade.
<table class="table table-hover">
<tr>
<th>ID</th>
<th>Item</th>
<th>Sales</th>
</tr>
<thead>
</thead>
<tbody>
#forelse($solditems as $solditem)
<tr>
<td>{{$solditem->id}}</td>
<td>{{$solditem->item}}</td>
<td>{{$solditem->subtotal}}</td>
</tr>
#empty
#endforelse
</tbody>
</table>
Now, what I want to do is to combine an item that has same item names or $solditem->item while adding up there subtotals.
For example;
ID #1 Apple = 50
ID #2 Apple = 80
Will become this;
ID #1 Apple = 130
I tried using groupBy on query builder so an item with same name will only show once, but I'm having problems designing an algorithm adding up the subtotal.

Try this one,
This gives you sum of cost with same item name for all items.
$solditems = DB::table('orders')
->where('status', 'served')
->select('orders.*',DB::raw('SUM() as total'))
->groupBy('orders.item')
->orderBy('id')
->get();

You could take advantage of Laravel's collection methods. You can use the GroupBy method to create grouped subarrays by product name and foreach of those group using the Sum method return the sum of the total column.

Related

Dynamic column/attribute. Stuck. Need inspiration

i’m looking for a solution to a problem that I’m having for a while now. Maybe you can inspire me to do this better. I’m trying not to make a basic mistake in the planning process therefore I’m asking you for advice.
I’m having a Contact::model which has few fixed attributes like id etc. Additionally I would like to have different attributes created dynamically for the whole Contact::model. Some user will be given the functionality to add attributes like name, email, address to the whole model. I’ve dropped the idea of programmatically updating the table itself by creating/dropping columns (this would introduce different problems). As for now i've created two additional tables. One with the additional column names [Columns::model] and a pivot table to assign the value to a Contact::model and Column::model.
To list all contacts i’m preparing the ContactColumn table as array where the first key is the contact_id and the second is the column_id, therefore i get the value. This introduces the n+1 issue. This would not be that bad, but with this approach it will be extremely hard (or resource consuming) to order the contacts by dynamic column values, filtering, searching etc.
Can you somehow guide me to a better solution. How can i merge the contact collection with the values for given columns so it looks like it was a fixed table?
<table>
<thead>
<tr>
<th>Fixed columns [i.e. ID]</th>
#foreach ($columns as $column)
<th>{{ $column->name }}</th>
#endforeach
</tr>
</thead>
<tbody>
#foreach ($contacts as $contact)
<tr>
<td>{{ $contact->id }}</td>
#foreach ($columns as $column)
<td>
#if (array_key_exists($column->id, $values[$contact->id]))
{{ $values[$contact->id][$column->id] }}
#endif
</td>
#endforeach
</tr>
#endforeach
</tbody>
</table>
And the $value array.
foreach (ColumnContact::all() as $pivot) {
$values[$pivot->contact_id][$pivot->column_id] = $pivot->value;
}
return $values;
Edit: I've solved it like this
$this->contacts = Contact::when($this->dynamicColumnName, function($query) {
$query->join('column_contact', function ($join) {
$join->on('id', '=', 'column_contact.contact_id')
->where('column_contact.column_id', '=', $this->dynamicColumnName->id);
})
->orderBy('value', $this->orderingDirection);
})
(...)
->paginate(self::PER_PAGE);
Apart from the fixed fields, add an extra JSON field in your schema called 'custom_fields'. Have a look into => https://github.com/stancl/virtualcolumn
Separate table for custom fields is not a good idea because then you have to handle model events separately and so on.

Laravel orderby with relationship

i'm trying to sort the student list by each level with using relationship method with OrderBy function but unfortunately i can't make it work any idea whats missing on my code?
Note:
every-time i remove the orderby my code will work but students level are not arrange accordingly
Controller:
$students=Student::with('level')->where(['status' => 'ENROLLED'])->get()->orderBy('level_name','asc');
View
<table>
<tr>
<th>Name</th>
<th>Level</th>
</tr>
#foreach($students as $std)
<tr>
<td>
{{$std->student_name}}
</td>
<td>
#foreach($std->level as $lv)
{{$lv->level_name}}
#endforeach
</td>
</tr>
#endforeach
</table>
You can't order by a relationship because under the hood laravel makes two seperate queries under the hood.
You can instead use a join, something like this (beware I guessed your table names, so you may have to update them).
$users = Student::join('levels', 'students.level_id', '=', 'levels.id')
->orderBy('levels. level_name', 'asc')->select('students.*')->paginate(10);
Try this:
Controller:
$students = Student::with(['level' => function (Builder $query) {
$query->orderBy('level_name', 'asc');
}])->where(['status' => 'ENROLLED'])->get();
In addition you can add orderBy() to relation method.
Student Model:
public function level()
{
return $this->relationMethod(Level::class)->orderBy('level_name', 'asc');
}
Try this
$students=Student::with('level')->where(['status' => 'ENROLLED'])->orderBy('level_name','asc')->get();

Complicated query from three tables in Laravel

I have a problem what I can’t solve with Laravel.
I have three table:
Users, Properties, Offers
Users haveMany Properties and Offers
Properties and Offers belongsTo a User
Properties table:
id
user_id
contract_start_date
other_date
…
created_at
updated_at
Offers table:
id
user_id
…
created_at
updated_at
I would like give a table that is like this (I use two date filter: startDate and endDate):
Users name || Properties count (They are filtered by contract_start_date) || Properties count (They are filtered by other_date) || Offers count
———
user1 || 5 || 2 || 12
user2 || 0 || 1 || 0
user3 || 0 || 0 || 0
…
I try with union, leftJoin, etc but I can’t solve this…
Thanks, if you can help
Quick and Dirty
First, get your users eager loading their properties' dates and all the offers.
$users = Users::with([
'property'=>function($query){
$query->select('contract_startdate','otherdate');
},
'offer'=>function($query){
$query->select('id');
},
);
Assuming you've properly set the $dates array in your model to include your contract_startdate and other_date. We can use carbon to filter the collection to get the properties we're interested in counting. In your view, you can:
<table>
<thead>
<tr>
<th>User</th>
<th>Property (start date)</th>
<th>Property (other date)</th>
<th>Offers</th>
</tr>
</thead>
<tbody>
#foreach($users as $user)
<tr>
<td>{{$user->name}}</td>
<td>{{$user->properties
->filter(function($item) use ($filter_start,$filter_end){
return $item->contract_startdate->between($filter_start,$filter_end);
})->count() }}</td>
<td>{{$user->properties
->filter(function($item) use ($filter_start,$filter_end){
return return $item->other_date->between($filter_start,$filter_end);
})->count() }}</td>
<td>{{$user->offers->count()}}</td>
</tr>
#endforeach
</tbody>
</table>
Cleaning that up
You should likely refactor the filter out of the view for cleanness, but doing so will add another loop over collection. But you might be able to remove a loop by doing something like this in your controller.
$users = Users::with([
'property'=>function($query){
$query->select('contract_startdate','otherdate');
},
'offer'=>function($query){
$query->select('id');
},
);
$byContractDate = collect();
$byOtherDate = collect();
foreach($users as $user){
foreach($properties as $property){
$contractCounter = 0;
$otherCounter = 0;
if($propery->contract_startdate->between($filter_start,$filter_end){
$contractCounter++;
}
if($propery->contract_startdate->between($filter_start,$filter_end){
$otherCounter++;
}
}
$byContractDate->put($user->id,$contractCounter);
$byOther->put($user->id,$otherCounter);
}
And in your view:
<table>
<thead>
<tr>
<th>User</th>
<th>Property (start date)</th>
<th>Property (other date)</th>
<th>Offers</th>
</tr>
</thead>
<tbody>
#foreach($users as $user)
<tr>
<td>{{$user->name}}</td>
<td>{{$byContract->get($user->id)}}</td>
<td>{{$byOther->get($user->id)}}</td>
<td>{{$user->offers->count()}}</td>
</tr>
#endforeach
</tbody>
</table>

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();

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