Modify Laravel Backpacker CRUD list view - php

I have Laravel 5.2 Backpacker admin for my new project and I need to make minor adjustments to the generated list view. Ie:
I have amount stored as cents in database, but would need to show as regular amount, so this would basically require to divide all values from amount column by 100;
I have certain rows, that have the cancelled date in them. I would like to set the row class to 'warning' for these.
So far I found only this complete override solution, but was wondering, if it could be done simpler in the crud controller.
I already can modify the header with this:
$this->crud->setColumnDetails('amount', ['label' => 'Total Amount']);
Is there such a simple option for data rows? Like:
$this->crud->setColumnData('amount', $this->crud->amount/100);

1) I'd recommend using an accesor, say:
public function getAmountInDollarsAttribute($value)
{
return ($this->amount)/100;
}
You will then be able to add a column for that attribute, "amountInDollars".
2) An easy way to achieve something similar would be to create a custom column. Inside it you will be able to show a warning/success label, which will make that row stand out. Something like:
<td>
#if ($entry->cancelled_date)
<span class="label label-danger">Cancelled</span>
#else
<span class="label label-default">Not cancelled</span>
#endif
</td>
Hope it helps. Cheers!

Related

Laravel retrieves too many model on a simple paginate query with loaded relations

I have a simple database with couple of tables. orders - the main table, about 16_000 records. Each order has payments. The order_payments table is about 17_000 records. Also each could have zero or more travellers in a order_tourists table - 33_000 rows.
The Order model looks like this
namespace App;
class Order extends \Illuminate\Database\Eloquent\Model
{
public function payments()
{
return $this->hasMany('App\OrderPayment');
}
public function tourists()
{
return $this->hasMany('App\OrderTourist');
}
}
I have a simple task to display a paginated list of all orders. Each item in this list should display some order's information (from the orders table), the sum of all payments and names of all travellers.
The first that came to my mind was something like this.
Get all orders with payments and travellers and pass this data to a view
Route::get('search_offers', static function () {
$orders = \App\Order::query()->with(['payments', 'tourists']);
return view('orders', [
'orders' => $orders->paginate(),
]);
});
And my orders.blade.php is pretty straightforward
<ol>
#foreach($orders as $order)
<li>
{{$order->request_id}}; {{ $order->payments->sum('amount') }}; {{ $order->tourists->implode('fullName', ', ') }}
</li>
#endforeach
</ol>
However, even the number of queries is very small I have a lot of manipulations with models. I add a custom hook to track all eloquent.* events and in first case there are more than 23_000 of such events.
Then I try to remove the loading of relations so for each order Laravel have to run a separate query to get all order's payments and travellers. I simply did like this in my controller
return view('orders', [
'orders' => \App\Order::query()->paginate(),
]);
So, I get more queries but page speed improves and a number of affected models significantly decreased
To track affected models I add a listner in a AppServiceProvider::boot method and output a AppServiceProvider::$hydratedModels value in a debugbar panel
Event::listen('eloquent.*', static function ($event) {
if (strpos($event, 'eloquent.retrieved') !== false) {
AppServiceProvider::$hydratedModels++;
}
});
So, my question how it possible that so many models are retrieved even if I display only 15 items. Looks like Laravel somehow get all of them, processed but retruns only those for a required page.
Right now it's not a big deal to run extra queries to fetch some data but I'm wondering maybe I'm doing something wrong and it's possible to load relations but do not cause Laravel to retrieve all models.
I found the issue. Thanks, #dparoli for a suggestion.
My models have a primary key as UUID. But I did not change a Model::$keyType so internally Laravel cast it into an integer and queries look like this.
Instead of a UUID string, I got integers as an order_id for related records.
So, I add protected $keyType = 'string'; and everything starts work as expected.
PS. Although, I'm wondering how it works before.

Fat Free Framework - Loading array of data from a query

I have no issues loading a unique record from my DB utilizing F3's ORM. However, when I want to retrieve multiple records for display or processing, the load method will still only fetch the first it finds.
I've found this on the docs:
By default, a data mapper's load() method retrieves only the first
record that matches the specified criteria. If you have more than one
that meets the same condition as the first record loaded, you can use
the skip() method for navigation
However the docs then continue only to show how to manipulate multiple records with built in find method, which returns an object filled with references to database entries.
I prefer not to loop through multiple records every time, I think there must be something I'm missing, how does one do this with fat free? In the docs it even references code where you can just do the following after a simply DB call, but I've yet to see how to do that with provided methods:
<repeat group="{{ #result }}" value="{{ #item }}">
<span>{{ #item.brandName }}</span>
</repeat>
What is the proper way to obtain a batch of records? For an example, please see below:
$productList = $products->load(array('created_on >= ?', $date->timestamp)); //Load most recently created products
or
$productList = $products->find(array('created_on >= ?',$date->timestamp),array('order'=>'created_on'));
In the example above, the date is utilizing Carbon, assume that is a proper timestamp.
I don't think there is a proper way to retrieve a bunch of records. The SQL mapper acts as a cursor. The best way to see how it works is with the load method: the mapper will retrieve all matching records and set its focus on the first one. You can do what you want with it and then call next to move on to the next one:
$products->load(array('created_on >= ?', $date->timestamp));
while (!$products->dry() ) {
// Do something with the record
$products->next();
}
The find method will return an array of records (actually they are also cursors, but each has only one record so you can treat them just as objects which are mapped to the records). Here, you will need to assign the result to a new variable like you did in your example:
$productList = $products->find(array('created_on >= ?',$date->timestamp),array('order'=>'created_on'));
// You can loop over the array to get some info or update the records:
foreach ($productList as $product) {
// Do something with the record
}
Or you can directly use the $productList variable in the template:
<repeat group="{{ #productList }}" value="{{ #item }}">
<span>{{ #item.brandName }}</span>
</repeat>

Laravel (OOP), model specfic array of values (for example titles of users)

I have model User and inside there is a field named title.
Now I want to define all possible titles. For example an array of
['0' => 'Mr.', '1' => 'Ms.']
What is the correct way to define these.
My idea is to create the public function titles() { return ['0' => 'Mr.', '1' => 'Ms.']; inside the User.php model and call for the array as $user->titles() whenever I need it. However this makes me twitch a bit because I'm calling the function on the model instance.
I really don't think creating a relation here is needed as there aren't more than ten possible titles.
Is there a better/right way to do these kind of things. I'm not that new to Laravel but I'm self thought so I'm trying to check my ways of doing stuff here.
I need to get into these oop basics a bit so any pointers here are helpfull.
Thanks in advance.
One way to do this is to keep titles in config file, like:
'titles' => [
1 => 'Mr.',
2 => 'Mrs.'
....
],
Benefits are:
you can edit this info at any time (keeping data hardcoded into model class is a bad practice)
you can use this list as is for select list building
you can keep data as TINYINT by keeping title IDs, sometimes it's a benefit
To build select list, do something like this:
{!! Form::select('titles', config('custom_config.titles'), 1) !!}
If you want dynamic select box write this code
$titles = User::lists('title', 'id'); // controller
{!! Form::select('title', $titles, null, ['class' => 'form-control']) !!} // view
You can use config files as #Alexey Mezenin suggested, but in this cases I prefer to use the database to store that kind of options (I mean in a different table, not in same users table), so I can add or delete options without touching the code and use the same form template/partial across different pages or projects if needed.
If you just need to retrieve the array it would be as easy as make a query in your controller and return it, but if you need it for form options I suggest you to use something like a form builder helper in which you get the form options from database and build your form using laravelcollective package, then you can pass the helper object to your view to show your fields and show whatever info you need.

Laravel 5 | For Loop w/Multiple Relations

I've setup a few model relationships and I'm trying to feed that data to a page that's displays a table with different columns to show the information. I've seeded 3 Tables (customers, jobs, steps) with Faker dummy data just for testing. How can I pull in the "Steps" table column data? My controller defines both (2) variables for the Job & Step model but I'm unsure how to pull in Step to the display. Do I have to do an array for the the variables called in the controller?
Here's my controller setup
public function index()
{
$jobs = Job::all();
$steps = Step::all();
return view('jobs.home', compact ('jobs', 'steps'));
}
Here's my blade setup
#foreach ($jobs as $job)
<tr>
<td>{{ $job->number }}</td>
<td>{{ $job->customer->name }}</td>
<td>NEED TO ADD STEPS COLUMN Here</td>
</tr>
#endforeach
There are several ways that this could be done, depending on what kind of data your "Step" model is providing for you.
If you just need 1 item from the "Step" model, you could first find just the specific data you need from the "Step" model instead of pulling in everything with Step::all(); for instance, you could use one of the more specific methods under "Collection Methods" that just return one item: https://github.com/susanBuck/notes/blob/master/05_Laravel/11_Databases_Eloquent_Collections.md
If you need more than 1 item, then you can use a more specific Collection method as well; however, you are going to have to loop through it to extract out each individual item. You will have to use "if" statements to determine which item is the one you want and then pull in its column data the same way you did with the "jobs" item like $job->number.
For your second question, you don't have to use an array to return the items from the controller. Just get the value you want there and return that if you like. For instance, to get the first "step" (if you just wanted the first one) then in the Controller do this to return a variable instead of an array:
public function index()
{
$firstStep = Step::first();
return view('jobs.home')->with('firstStep', $firstStep);
}
and then in your Blade file, you can access the data column rows just like you did with your $job variable:
{{ $firstStep->someColumn}}

Populating a dropdown menu with database results in Laravel 4

I'm trying to populate a drop down menu with database results in Laravel 4. I'm extremely new to Laravel. This is actually my first site and I'm learning as I go. So, please tell me if I'm using the wrong terminology or not enough information.
I've got a database of company info and I need users to be able to choose a company from a dropdown. Or if the company isn't in the database to add it.
For the select menu, it needs to go like this:
[company name result]
And I'm using this code in my controller:
$companies = RecordCompany::get();
$company_selector = array();
foreach($companies as $company) {
$company_selector[$company->id] = $company->id;
$company_selector[$company->company_name] = $company->company_name;
}
return View::make('admin.record_new', array('company_selector' => $company_selector));
And this is what I've got in my view:
#if(count($client_selector)>0)
{{ Form::select('company_id', $company_selector, array_values($company_selector)[0]) }}
#endif
Disclaimer: I found this code online.
First, I don't understand how it will populate the value and option text without my telling it where to put the data.
Second, the error that's coming back is unexpected . When I take out the [0] in the form code, it tells me that $company_selector is undefined.
What am I doing wrong here?
In order to populate a dropdown menu with all the records from the RecordCompany model, you can do the following, in your view:
{{ Form::select('company_id', RecordCompany::lists('company_name', 'id')) }}
Note: In Laravel 5, the method lists has been deprecated. Use
pluck instead.
Explanation of the code:
The Form::select methods creates a HTML select tag.
company_id is the name of the select tag.
The second parameter is the options for the select tag. The lists method in any model (RecordCompany in this case) generates an associative array containing the parameters passed to that method (id and company_name in this case) of all the records in the model's database table.
If you want, you can also call the lists method from the controller and then pass the value to the view, like following:
In Controller
$company_lists = RecordCompany::lists('company_name', 'id');
return View::make('admin.record_new', array('company_lists' => $company_lists));
In View
{{ Form::select('company_id', $company_lists) }}
You can view the Laravel 4 documentation for generating a drop down list here: http://laravel.com/docs/html#drop-down-lists
I'm severelly against using DB calls in views. And here is why:
It ain't made for that!.
Period.
If I where you (note the if clause) I'd like better to fulfill a regular array, being the company->id the array key and any other information you may wanna for that especific key as a value. On my blade code, I'd made that way:
{{ Form::select('company_id', $companies) }}
Where "companies" would be a array passed as argument to the view by the controller.
Views aren't made to make DB consults. They are made to display data. JUST IT!
That being said:
The first argument on the Form::select is the selector's name. The one you get on the Input::get.
The second argument is the list for fulfill the select attribute (we already talked about it up there!)
And the third, non less important, is where you say which one comes selected on loading page (used for editions). You have to reference the identifier (the company id, in that case). It's optional, for obvious reasons.
If I didn't made myself clear, please ask down here! =D
For Laravel 5, you can code like this :-
Controller Code
$company_lists = RecordCompany::pluck('company_name', 'id');
return View::make('admin.record_new', $company_lists);
View Code
{{ Form::select('company_id', $company_lists) }}

Categories