Laravel get more data in select list - php

I am making a select list of my users, but my user are stored in my database with a lastname and firstname. And all the documentation on lists use only one column item from te database. How do I get both these names in my list?
Controller
public function make() {
$users = User::lists('lastname','id');
return View::make('admin.awardAchievement')->with('users', $users);
}
View
{{ Form::select('user', $users , Input::old('user')) }}
This wil only print out the lastname of my users, but I want the list to show the firstname and lastname.. Can someone please help?

You can do this by using a custom DB expression and concatenate lastname + id. This because the Form helper only likes 1 column for the text (option text) and 1 column for the id (option value attribute).
$users = User::select(DB::raw('CONCAT(lastname, id) AS userselect, id'))->lists('userselect', 'id');
I find this the way to go, except you have to keep in mind that CONCAT can behave differently on different DBMS's or this function could not even exist. If you do care about this, I would just loop through your users list and manually concatenate with standard PHP script.
Another option is to just adjust the Form helper to support your needs. Though I would not recommend doing this if you want to keep your code rapidly upgradable.
EDIT: Check first comment

You can add a setter to your model, so something like:
public function getFullNameAttribute()
{
return $this->attributes['firstname'] . ' ' . $this->attributes['lastname'];
}
and then you can just do
$users = User::lists('fullname','id');

Related

Getting the rows created before the selected one

I was wondering about the best way to get the count of all the rows created before the selected one. Right now I have defined an accessor that looks like this:
// In the model
public function getPositionAttribute() {
return self::where([
// Some other condition
['created_at', '<', $this->created_at->toDateTimeString()]
])->count();
}
// In the code
$model->position
It works correctly, but I'm worried about 2 things:
Is it a bad practice to call self on the model? Looks somehow off to me.
When called in a foreach this obviously generates a query for each element which is far from optimal. Is there any way to refactor this so that it can be eager loaded in a single query?
Bonus: I have totally discarded the idea of keeping a column with some kind of index because that initially sounded impossible to maintain, eg. when a record is deleted all the others should somehow shift position. Should I reconsider it? Is there a better way?
Pretty sure that using self here is the "best practice" because that is how that keyword was designed to be used.
In regards to refactoring, i personally can't think of optimizing the query as is but instead you could create a function that preloads all the position then use it normally. Assuming your model has a unique key 'id' and you are passing in a collection of model then, you can try something like this:
public static function populateOrderPositions($modelCollection){
// Optimize this query to include your "other condition"
$idCollection = Model::orderBy('created_at') // this will make it in the order of creation
->pluck('id'); // this will only retrieve the id field
// This array will contain an array with the model object ids as key and a numeric position (starts at 0)
$positionArr = $idCollection->flip()->all();
// Then just load all the position into the object and return it.
return $modelCollection->map(function($modelObj) use ($positionArr){
return $modelObj->position = $positionArr[$modelObj->id] + 1; // +1 because array key starts at 0
};
}
You would also need to adjust your attribute code to use the loaded attribute instead of ignoring the loaded attribute like so:
public function getPositionAttribute() {
return $this->attributes['position'] ?? self::where([
// Some other condition
['created_at', '<', $this->created_at->toDateTimeString()]
])->count();
}
With these changes, you can then prepopulate the position then use it afterward without the need to query the database.
These code are untested as i don't know how your model and query will be structured and is more of an example. Also you would need to compare the performance vs your original code.

storing array data in database using laravel

I want to store the array data in a database, but in my database I'm getting only the first input tag of the form:
here is my controller:
public function store(Request $request)
{
$qt = Qt::all();
$Itemname = $request->input('Itemname');
$Quantity = $request->input('Quantity');
$Price = $request->input('Price');
$Tax = $request->input('Tax');
$Total = $request->input('Total');
$GrandTotal = $request->input('GrandTotal');
$data = array('Itemname'=>$Itemname,'Quantity'=>$Quantity,'Price'=>$Price,'Tax'=>$Tax,'Total'=>$Total);
dd($data);
Qt::table('qts')->insert($data);
return redirect()->route('quotes.index');
}
I'm not sure what you mean by 'one data' - if you are only storing one field, that shouldn't be happening. The way you have this set up, it is data for one row. Thus, the expected output to the database would be a single Qt model saved. This would be what I would expect in a store() method based on a user entering a single item (with name, price, tax, etc). One form for one new Qt. And I think you have it almost set up for this.
However, Laravel makes the storage of a new model much easier. You don't need pretty much any of that code, because it looks like you have your incoming form fields set to correctly match the name of the fields on the Qt model in the database. Though, you might want to make them lowercase on both to match convention.
If the form fields are a match to the database fields on the model, you can remove almost everything in that method and replace it with:
public function store(Request $request)
{
Qt::create($request->all());
return redirect()->route('quotes.index');
}
The create method will take the $request object as is, and automatically set the right elements to the right fields if they are set to fillable on the model.
EDIT:
I understand you are looking to make multiple rows of items for a single customer based on many items coming in through your form. However, I don't know where that information is coming from -- I don't know where the array of items is within your form (or $request object). This might be your reason why you are having an error in the first place: I don't see how to loop or find multiple items, I just see one item coming in, which would produce one row into the database based on that form. The above code is correct based on what you have said is coming from the form.
However, based on the parameterize() error you mention in the comments, you likely have an array somewhere that is causing the parameter issue, and which would help you to create multiple rows. You don't show what your array is, but you mention that these many items would be attached to a single customer.
Here is a possible way to do this. I will make up a few variables based on what you've said (some array to loop on, that there is a 'customer' object or similar, etc.)
public function store(Request $request)
{
$customer = Customer::find($request->get('customer_id'));
// I don't know how you bring in the customer's id - maybe as a $request item or perhaps through GET?
$newItemsArray = []; // Store the rows of new items here
// Whatever the array of items is, pull it and loop on it to create rows of items:
foreach($request->get('someItemsArray') as $newItem){
// Assuming the name of the array fields match the database field names:
// Assumes $newItem is an associative array
$newItemsArray['Itemname'] = $newItem['Itemname'];
$newItemsArray['Quantity'] = $newItem['Quantity'];
$newItemsArray['Price'] = $newItem['Price'];
$newItemsArray['Tax'] = $newItem['Tax'];
$newItemsArray['Total'] = $newItem['Total'];
}
// Here is where you can create all the rows and attach them at the same time to your customer
$customer->Qts()->createMany($newItemsArray);
return redirect()->route('quotes.index');
}
This will allow for creating multiple rows of items and attach to a customer all at once.
You'll need to fill in the blanks - how are you sending the multiple rows from your form, how are you sending the customer who is adding the items, how to deal with GrandTotal (is it an array item, or is it a single field passed from the form, based on a calculation), etc. But, this will answer your question on the controller side to get multiple rows in for a customer.

Laravel/Eloquent - Using dynamic properties in a multi-field search form

I have a search form in my Laravel app that searches through a list of properties. My properties table has a Model already, and is connected to another Model/table called details, which contains things like if a certain property has a garden, garage etc, through a one-to-many relationship. When doing index or show stuff this works fine - I can get the details for a property using $property->details.
The search form has a field where the user can search for such details, and this is where I'm struggling. I don't just need to find the detail itself, I need to first see if anything present in the input matches anything in the Details table, then see if the foreign key (called prop_id) matches any of my Property id's, then include those properties in the results. Something like:
$query = Property::where('archived',0);
$query->details->where('text',$keyword);
$results = $query->get();
The reason this won't work is because details isn't a property of $query, but I'm not sure what to use as an alternative as calling Property::where() again would surely reset the whole query. I'm storing it all in a $query variable because all the fields in my form are optional, so I'm building the query based only on the inputs that have values in.
Bit stumped here, so if anyone has already tackled something like this, the tips would be appreciated. Cheers!
Use whereHas:
$query = Property::where('archived', 0)->whereHas('details', function ($q) use ($keyword)
{
$q->where('text', $keyword);
});
$results = $query->get();
You may try something like this:
$query = Property::where('archived', 0);
if(isset($keyword)) {
$query->whereHas('details', function ($q) use ($keyword) {
$q->where('text', $keyword);
});
}
$results = $query->get();

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

In CakePHP, how can you determine if a field was changed in an edit action?

I'm using the cacheCounter in CakePHP, which increments a counter for related fields.
Example, I have a Person table a Source table. Person.source_id maps to a row in the Source table. Each person has one Source, and each Source has none or many Person rows.
cacheCounter is working great when I change the value of a source on a person. It increments Source.Person_Count. Cool.
But when it increments, it adds it to the destination source for a person, but doesn't remove it from the old value. I tried updateCacheControl() in afterSave, but that didn't do anything.
So then I wrote some code in my model for afterSave that would subtract the source source_id, but it always did this even when I wasn't even changing the source_id. (So the count went negative).
My question: Is there a way to tell if a field was changed in the model in CakePHP?
To monitor changes in a field, you can use this logic in your model with no changes elsewhere required:
function beforeSave() {
$this->recursive = -1;
$this->old = $this->find(array($this->primaryKey => $this->id));
if ($this->old){
$changed_fields = array();
foreach ($this->data[$this->alias] as $key =>$value) {
if ($this->old[$this->alias][$key] != $value) {
$changed_fields[] = $key;
}
}
}
// $changed_fields is an array of fields that changed
return true;
}
With reference to Alexander Morland Answer.
How about this instead of looping through it in before filter.
$result = array_diff_assoc($this->old[$this->alias],$this->data[$this->alias]);
You will get key as well as value also.
You could use ->isDirty() in the entity to see if a field has been modified.
// Prior to 3.5 use dirty()
$article->isDirty('title');
check the doc: https://book.cakephp.org/3/en/orm/entities.html#checking-if-an-entity-has-been-modified
Edits happen infrequently, so another select before you do the update is no big deal, so, fetch the record before you save, save it, compare the data submitted in the edit form with the data you fetched from the db before you saved it, if its different, do something.
In the edit view, include another hidden field for the field you want to monitor but suffix the field name with something like "_prev" and set the value to the current value of the field you want to monitor. Then in your controller's edit action, do something if the two fields are not equal. e.g.
echo $form->input('field_to_monitor');
echo $form->hidden('field_to_monitor_prev', array('value'=>$form->value('field_to_monitor')));
See if the "save" uses some sort of DBAL call that returns "affected rows", usually this is how you can judge if the last query changed data, or if it didn't. Because if it didn't, the affected rows after an UPDATE-statement are 0.
You can call getAffectedRows() on any model class.
From class Model :
/**
* Returns the number of rows affected by the last query
*
* #return int Number of rows
* #access public
*/
function getAffectedRows() {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
return $db->lastAffected();
}

Categories