Fat Free Framework - Loading array of data from a query - php

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>

Related

Yii2 - Calling model functions on entire activeRecords?

What I am trying to do
I want to query a specific set of records using active model like so
$jobModel = Jobs::find()->select('JOB_CODE')->distinct()->where(['DEPT_ID'=>$dept_id])->all();
Then I want to assign a flag attribute to the records in this activerecord based on whether they appear in a relationship table
What I have tried
So in my job model, I have declared a new attribute inAccount. Then I added this function in the job model that sets the inAccount flag to -1 or 0 based on whether a record is found in the relationship table with the specified account_id
public function assignInAccount($account_id){
if(JobCodeAccounts::find()->where(['JOB_CODE'=>$this->JOB_CODE])->andWhere(['ACCOUNT_ID'=>$account_id])->one() == null){
$this->inAccount=0;
}
else{
$this->inAccount = -1;
}
}
What I have been doing is assigning each value individually using foreach like so
foreach($jobModel as $job){
$job->assignInAccount($account_id);
}
However, this is obviously very slow because if I have a large number of records in $jobModel, and each one makes a db query in assignInAccount() this could obviously take some time if the db is slow.
What I am looking for
I am wondering if there is a more efficient way to do this, so that I can assign inAccount to all job records at once. I considered using afterFind() but I don't think this would work as I need to specify a specific parameter. I am wondering if there is a way I can pass in an entire model (or at least array of models/model-attributes and then do all the assignations running only a single query.
I should mention that I do need to end up with the original $jobModel activerecord as well
Thanks to scaisEdge's answer I was able to come up with an alternative solution, first finding the array of jobs that need to be flagged like so:
$inAccountJobs = array_column(Yii::$app->db->createCommand('Select * from job_code_accounts where ACCOUNT_ID = :account_id')
->bindValues([':account_id' => $account_id])->queryAll(), 'JOB_CODE');
and then checking each job record to see if it appears in this array like so
foreach($jobModel as $job){
if(in_array($job->JOB_CODE, $inAccountJobs))
$job->inAccount = -1;
else
$job->inAccount = 0;
}
Does seem to be noticeably faster as it requires only a single query.

Modify Laravel Backpacker CRUD list view

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!

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}}

Eloquent - Updating all models in a collection

I want to set a certain attribute in all the models of a collection.
in plain SQL:
UPDATE table SET att = 'foo' WHERE id in (1,2,3)
the code i have:
$models = MyModel::findMany([1,2,3]);
$models->update(['att'=>'foo']);
taken from here
but doesn't work. I'm getting
Call to undefined method Illuminate\Database\Eloquent\Collection::update()
the only way i have found it's building a query with the query builder but i'd rather avoid that.
You are returning a collection, not keeping the query open to update. Like your example is doing.
$models = MyModel::whereIn('id',[1,2,3]);
$models->update(['att'=>'foo']);
whereIn will query a column in your case id, the second parameter is an array of the ids you want to return, but will not execute the query. The findMany you were using was executing it thus returning a Collection of models.
If you need to get the model to use for something else you can do $collection = $models->get(); and it will return a collection of the models.
If you do not just simply write it on one line like so;
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
Another option which i do not recommend is using the following;
$models = MyModel::findMany([1,2,3]);
$models->each(function ($item){
$item->update(['att'=>'foo']);
});
This will loop over all the items in the collection and update them individually. But I recommend the whereIn method.
The best solution in one single query is still:
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
If you already have a collection of models and you want to do a direct update you can use modelKeys() method. Consider that after making this update your $models collection remains outdated and you may need to refresh it:
MyModel::whereIn('id', $models->modelKeys())->update(['att'=>'foo']);
$models = MyModel::findMany($models->modelKeys());
The next example I will not recommend because for every item of your $models collection a new extra query is performed:
$models->each(function ($item) {
$item->update(['att'=>'foo']);
});
or simpler, from Laravel 5.4 you can do $models->each->update(['att'=>'foo']);
However, the last example (and only the last) is good when you want to trigger some model events like saving, saved, updating, updated. Other presented solutions are touching direct the database but models are not waked up.
Just use the following:
MyModel::query()->update([
"att" => "foo"
]);
Be mindful that batch updating models won't fire callback updating and updated events. If you need those to be fired, you have to execute each update separately, for example like so (assuming $models is a collection of models):
$models->each(fn($model) => $model->update(['att'=>'foo']) );

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