Laravel Form Model Binding with Relationships - laravel

Is it possible to bind a form with a model that has relationships? For example I have a Order model that has a one to many with a Details model. That would save a lot of time with
#foreach($order->details as $detail)
{{ Form::text('product_name', Input::old('product_name') ? Input::old('product_name') : detail->product_name)
#endforeach

For a one-to-one relation it's possible to use something like this:
Form::text('detail[product_name]')
In this case $order->detail->product_name will be populated in the given text box if an instance of Order model is bound to the from using Form::model($order) with the related model Detail but it may not possible for one-to-many because simply there will be a collection and you need a loop.

To complete the answer of #WereWolf..
Make an array of product_name detail_names
Input class allow you to access nested array by dot notation, eg: orders.1.product_name
Don't forget the second argument of Input::old() or Input::get()
is the default value, so you can specify the DB value and avoid conditional test..
.
Form::text('detail_names['.$detail->id.']', Input::old('detail_names.'.$detail->id, $detail->product_name))
In your controller, something like that:
foreach(Input:get('detail_names') as $id => $product_name)
{
//...
}
Hope this will help you to save a bit of time.

Related

How to dissociate elements from a HasMany relation?

There's the save and saveMany methods on the HasMany relation class, but where are the dissociate(Many)/detach(Many) methods? There's also no built-in way to get the inverse relationship method, so what's the best way to dissociate an array of id's/models from a HasMany relationship object.
Currently I'm using:
$hasMany = $parent->theRelationship(); // Get the relationship object.
$child = $hasMany->getRelated(); // Get an empty related model.
$key = $hasMany->getForeignKeyName(); // Get the name of the column on the child to set to NULL.
$child->findMany($IDs)->each(function($model) use ($key) {
$model->$key = NULL;
$model->save();
});
This could be alot shorter with something like:
$hasMany = $parent->theRelationship();
$hasMany->dissociate($IDs);
Bonus points if you have any official answers from Taylor as to why he hasn't implemented this, I've seen him close feature requests of this kind on GitHub.
I am not sure why there isn't a function, but to be more performant than your example, you could use the DB class like:
\DB::table('child_table')->where('parent_id', $parent->id)->update(['parent_id' => null]);
You could use detach like so;
$parent->theRelationship()->detach([1,2,3])
Where you pass an array of IDs.
From Laravel documentation:
"For convenience, attach and detach also accept arrays of IDs as input"
The performatic way (1 db update):
$partent->theRelationship()->update(['parent_id' => null]);
The readable way (multiple db updates):
$parent->theRelationship->each->parentRelationship()->dissociate();

ManyToMany with and whereIn

I have a ManyToMany relationship between AdInterest and AdInterestGroup models, with a belongsToMany() method in each model so I can use dynamic properties:
AdInterest->groups
AdInterestGroup->interests
I can find all the "interests" in a single group like this:
$interests = AdInterestGroup::find(1)->interests->pluck('foo');
What I need is a merged, deduplicated array of the related 'foo' field from multiple groups.
I imagine I can deduplicate with ->unique(), but first, as you'd expect, this:
AdInterestGroup::whereIn('id',[1,2])->interests->get();
throws:
Property [interests] does not exist on the Eloquent builder instance.
The advice seems to be to use eager loading via with():
AdInterestGroup::with('interests')->whereIn('id',[1,2])->get();
Firstly, as you'd expect that's giving me an array of two values though (one for each ID).
Also, if I try and pluck('foo') again, it's looking in the wrong database table: from the AdInterestGroup table, rather than the relationship (AdInterest).
Is there a nice, neat Collection method / pipeline I can use to combine the data and get access to the relationship fields?
Use pluck() and flatten():
$groups = AdInterestGroup::with('interests')->whereIn('id', [1, 2])->get();
$interests = $groups->pluck('interests')->flatten();
$foos = $interests->pluck('foo')->unique();

How to select specific columns in laravel eloquent

lets say I have 7 columns in table, and I want to select only two of them, something like this
SELECT `name`,`surname` FROM `table` WHERE `id` = '1';
In laravel eloquent model it may looks like this
Table::where('id', 1)->get();
but I guess this expression will select ALL columns where id equals 1, and I want only two columns(name, surname). how to select only two columns?
You can do it like this:
Table::select('name','surname')->where('id', 1)->get();
Table::where('id', 1)->get(['name','surname']);
You can also use find() like this:
ModelName::find($id, ['name', 'surname']);
The $id variable can be an array in case you need to retrieve multiple instances of the model.
By using all() method we can select particular columns from table like as shown below.
ModelName::all('column1', 'column2', 'column3');
Note: Laravel 5.4
You first need to create a Model, that represent that Table and then use the below Eloquent way to fetch the data of only 2 fields.
Model::where('id', 1)
->pluck('name', 'surname')
->all();
Also Model::all(['id'])->toArray() it will only fetch id as array.
Get value of one column:
Table_Name::find($id)->column_name;
you can use this method with where clause:
Table_Name::where('id',$id)->first()->column_name;
or use this method for bypass PhpStorm "Method where not found in App\Models":
Table_Name::query()->where('id','=',$id)->first()->column_name;
in query builder:
DB::table('table_names')->find($id)->column_name;
with where cluase:
DB::table('table_names')->where('id',$id)->first()->column_name;
or
DB::table('table_names')->where('id',$id)->first('column_name');
last method result is array
You can use get() as well as all()
ModelName::where('a', 1)->get(['column1','column2']);
From laravel 5.3 only using get() method you can get specific columns of your table:
YouModelName::get(['id', 'name']);
Or from laravel 5.4 you can also use all() method for getting the fields of your choice:
YourModelName::all('id', 'name');
with both of above method get() or all() you can also use where() but syntax is different for both:
Model::all()
YourModelName::all('id', 'name')->where('id',1);
Model::get()
YourModelName::where('id',1)->get(['id', 'name']);
To get the result of specific column from table,we have to specify the column name.
Use following code : -
$result = DB::Table('table_name')->select('column1','column2')->where('id',1)->get();
for example -
$result = DB::Table('Student')->select('subject','class')->where('id',1)->get();
use App\Table;
// ...
Table::where('id',1)->get('name','surname');
if no where
Table::all('name','surname');
If you want to get a single value from Database
Model::where('id', 1)->value('name');
Also you can use pluck.
Model::where('id',1)->pluck('column1', 'column2');
You can use Table::select ('name', 'surname')->where ('id', 1)->get ().
Keep in mind that when selecting for only certain fields, you will have to make another query if you end up accessing those other fields later in the request (that may be obvious, just wanted to include that caveat). Including the id field is usually a good idea so laravel knows how to write back any updates you do to the model instance.
You can get it like
`PostModel::where('post_status', 'publish')->get(['title', 'content', 'slug', 'image_url']`)
link
you can also used findOrFail() method here it's good to used
if the exception is not caught, a 404 HTTP response is automatically sent back to the user. It is not necessary to write explicit checks to return 404 responses when using these method not give a 500 error..
ModelName::findOrFail($id, ['firstName', 'lastName']);
While most common approach is to use Model::select,
it can cause rendering out all attributes defined with accessor methods within model classes. So if you define attribute in your model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Get the user's first name.
*
* #param string $value
* #return string
*/
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
}
And then use:
TableName::select('username')->where('id', 1)->get();
It will output collection with both first_name and username, rather than only username.
Better use pluck(), solo or optionally in combination with select - if you want specific columns.
TableName::select('username')->where('id', 1)->pluck('username');
or
TableName::where('id', 1)->pluck('username'); //that would return collection consisting of only username values
Also, optionally, use ->toArray() to convert collection object into array.
If you want to get single row and from the that row single column, one line code to get the value of the specific column is to use find() method alongside specifying of the column that you want to retrieve it.
Here is sample code:
ModelName::find($id_of_the_record, ['column_name'])->toArray()['column_name'];
If you need to get one column calling pluck directly on a model is the most performant way to retrieve a single column from all models in Laravel.
Calling get or all before pluck will read all models into memory before plucking the value.
Users::pluck('email');
->get() much like ->all() (and ->first() etc..) can take the fields you want to bring back as parameters;
->get/all(['column1','column2'])
Would bring back the collection but only with column1 and column2
You can use the below query:
Table('table')->select('name','surname')->where('id',1)->get();
If you wanted to get the value of a single column like 'name', you could also use the following:
Table::where('id', 1)->first(['name'])->name;
For getting multiple columns (returns collection) :
Model::select('name','surname')->where('id', 1)->get();
If you want to get columns as array use the below code:
Model::select('name','surname')->where('id', 1)->get()->toArray();
If you want to get a single column try this:
Model::where('id', 1)->first(['column_name'])->column_name;

Laravel Eloquent Model Return Human value

I Have a User model with the property is_active.
When i'm inside a foreach loop, I don't want to use the following:
echo if ( $user->is_active == 0 ) 'not active' : 'active'
What will be the best way to implement this? write a isActive() method on the User model and place the logic there?
There are a few approaches that you can take.
Approach 1: Acessor!
Create on your model:
public function getFormattedActiveMethod() {
return $this->attributes['active'] ? 'active' : 'not active';
}
And then you can use it like: $user->formattedActive (or whathever you want to call it, as long your method name matches getCamelCaseNameAttribute()).
You can also add this attribute into the $appends property array, so it will show up when you cast the model to array/json.
Approach 2: Presenter Pattern
Simply create a new class just to present data to your views. It's a good approach to prevent the Model from bloating and also separate your logic.
There are a few packages that helps you to achieve it, like robclancy/presenter, laracasts/Presenter, ShawnMcCool/laravel-auto-presenter and even League's Fractal, that can help you building a consistent API too.
Depending on the size of your project, it's better to take this approach.

To Array or Not To Array?

In my model I get data using something like:
$this->all();
This is then returned to my controller which makes a view:
return View::make('myView')
->with('data', $this->myModel->getAll());
My question is, what's best practice, should the model return eloquent object or an array? By calling ->toArray()?
Short answer is to leave as objects. It seems silly to me to convert to arrays when the objects can be used in the view.
Consider if you might need to foreach through a model's relationships - using an array you either don't have this or have to preload it (even if you won't always have to use it), using an object, you can choose to use the relationship if you want to.
Now, I'm aware this is related to your previous question regarding passing arrays vs. object to the view, but that is a different question entirely. In that question you're basically saying "sometimes I have one object and sometimes I have a collection of objects, how do i handle this in the view". To which my answer would be that you sure ensure the view always sees a collection (or array or whatever), but never to actually convert an object to an array.
In that situation, in the case where you only get one object, just wrap that object in a collection (or array) before it goes to the view and there you go - normalised data done easily.
To wrap your result in a collection
There are many ways of doing this which depend on how you're getting the data in the first place. If you're doing your own Eloquent calls then the simplest solution is to always use ->get() rather than sometimes using ->find() or ->first(). If you use ->get() even in times you expect a single result, it'll return a single result wrapped in a collection already.
However, if you're provided with this someties-object-sometimes-array then you'll have to manually do it. Again this has two different but very similar tehcniques which depends on whether the data is compatible with Eloquent and Collection or whether it's more raw PHP objects and arrays.
Eloquent-compatible
if ($data instanceof \Illuminate\Databse\Eloquent\Model) {
$data = new \Illuminate\Database\Eloquent\Collection($data);
}
Standard objects and arrays
if (!is_array($object)) {
$data = array($data);
}
It's really as simple as that.
In my opinion, the cleaner, elegant way is always returning an object.
In a model I would do something like this
class Employee extends Eloquent {
protected $table = 'employees';
}
In the controller
public function index(){
$employees= Employee::get();
return View::make('index')->with('employees', $employees);
}
In the view:
#foreach($employees as $employee)
{{ $employee->name }}
#endforeach
Simply, it depends what do you want to do with returned data. In most cases, we are traversing returned array in a view (with blade), and appending it to some HTML list. I had some issues with passed data to view and unit testing.
Simply, test is not accepting object, so I explicitly had to pass array.

Resources