Laravel Eloquent: merge model with Input - php

I would like to know how it is possible to merge data from Input::all() with a model and save the result.
To clarify: I would like to do something like below:
$product = Product::find(1); // Eloquent Model
$product->merge( Input::all() ); // This is what I am looking for :)
$product->save();

You should use update method:
$product->update(Input::all());
But I recommend to use only method instead
$product->update(Input::only('name', 'type...'));

Use the model's fill() method for greater control. This lets us change attributes after merging the values before we save:
$product->fill($request->all());
$product->foo = 'bar';
$product->save();
If we've properly defined the model's $fillable attributes, there's no need to use Input::only(...) (or $request->only(...) in newer versions).

In addition to Razor's answer, if you need to create a new model you can use:
$product = Product::create(Input::all());

I think the better way now is to use FormRequest and the validated function like this :
public function update(Product $product, ProductFormRequest $request)
{
$product->update($request->validated());
//...
}

Related

Laravel 5.6: Invoking eloquent relationships change the collection data

Is there a way to invoke eloquent relationship methods without changing the original eloquent collection that the method runs on? Currently I have to employ a temporary collection to run the method immutable and to prevent adding entire related record to the response return:
$result = Item::find($id);
$array = array_values($result->toArray());
$temp = Item::find($id);
$title = $temp->article->title;
dd($temp); //This prints entire article record added to the temp collection data.
array_push($array, $title);
return response()->json($array);
You are not dealing with collections here but with models. Item::find($id) will get you an object of class Item (or null if not found).
As far as I know, there is no way to load a relation without storing it in the relation accessor. But you can always unset the accessor again to delete the loaded relation (from memory).
For your example, this process yields:
$result = Item::find($id);
$title = $result->article->title;
unset($result->article);
return response()->json(array_merge($result->toArray(), [$title]));
The above works but is no very nice code. Instead, you could do one of the following three things:
Use attributesToArray() instead of toArray() (which merges attributes and relations):
$result = Item::find($id);
return response()->json(array_merge($result->attributesToArray(), [$result->article->title]));
Add your own getter method on the Item class that will return all the data you want. Then use it in the controller:
class Item
{
public function getMyData(): array
{
return array_merge($this->attributesToArray(), [$this->article->title]);
}
}
Controller:
$result = Item::find($id);
return response()->json($result->getMyData());
Create your own response resource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ItemResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'title' => $this->article->title,
'author' => $this->article->author,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
Which can then be used like this:
return new ItemResource(Item::find($id));
The cleanest approach is option 3. Of course you could also use $this->attributesToArray() instead of enumerating the fields, but enumerating them will yield you security in future considering you might extend the model and do not want to expose the new fields.
I see two ways you can achieve that.
First, you can use an eloquent Resource. Basically it'll allow you to return exactly what you want from the model, so in your case, you'll be able to exclude the article. You can find the documentation here.
The second way is pretty new and is still undocumented (as fas i know), but it actually works well. You can use the unsetRelation method. So in your case, you just have to do:
$article = $result->article; // The article is loaded
$result->unsetRelation('article'); // It is unloaded and will not appear in the response
You can find the unsetRelation documentation here
There is not as far as I know. When dealing with Model outputs, I usually construct them manually like this:
$item = Item::find($id);
$result = $item->only('id', 'name', 'description', ...);
$result['title'] = $item->article->title;
return $result;
Should you need more power or a reusable solution, Resources are your best bet.
https://laravel.com/docs/5.6/eloquent-resources#concept-overview

Laravel How to display $hidden attribute on model on paginate

I'm using Laravel 5.5.
I read about this and know this function and it works makeVisible
$hidden = ['password', 'remember_token', 'email'];
I can display email using
$profile = auth()->user()->find($request->user()->id);
$profile->makeVisible(['email']);
On the frontend email is displayed. But it not works on many results like
// Get all users
$users = User::with('role', 'level')->makeVisible(['email'])->paginate(10); // Doesn't work
Also try this method from Laracasts toJson it works but I can't do it using paginate. Can you provide other methods or how to solve this? My aim is to display email column that is hidden. Thanks.
Another, possible easier solution depending on your requirements, is to call makeVisible on the collection:
// Get all users
$users = User::with('role', 'level')->paginate(10)->makeVisible(['email']);
You can also use this with find or get:
$profile = auth()->user()->find($request->user()->id)->makeVisible(['email']);
I solve this using this method.
Users.php on model
public function toArray()
{
// Only hide email if `guest` or not an `admin`
if (auth()->check() && auth()->user()->isAdmin()) {
$this->setAttributeVisibility();
}
return parent::toArray();
}
public function setAttributeVisibility()
{
$this->makeVisible(array_merge($this->fillable, $this->appends, ['enter_relationship_or_other_needed_data']));
}
and on controller just a simple
return User::with('role', 'level')->paginate(10);
I've read where pagination comes from toArray before creating pagination. Thanks for all your help. Also helps
You can use this:
$paginator = User::with('role', 'level')->paginate($pageSize);
$data = $pagination->getCollection();
$data->each(function ($item) {
$item->setHidden([])->setVisible(['email']);
});
$paginator->setCollection($data);
return $paginator;
You can try to use this approach. Using API Resources.
API Resources lets you format the data the way you want. You can create multiple Resource object to format in different ways your collections.
Set visible your parameter (in this case email) and when you need to return that item you can use a different Resource object that returns that elemement.
So when no need for email:
$users = User::with('role', 'level')->paginate(10);
return UserWithoutEmail::collection($users);
when email is needed:
$users = User::with('role', 'level')->paginate(10);
return UserWithEmail::collection($users);

laravel orm: different result from almost same query

I've a very big doubt about how works laravel for a very simple thing:
If I call:
$companies=User::All();
Then I can use statement like this in a forach:
foreach($companies as $company)
$company['new_field']= 'something';
If i'm limiting the output of the query like:
$companies = DB::table('companies')
->select('id','name','email','business_name',...)->get();
The things doesnt work as before,
I try with or without the ->get()
I try to convert with ->toArray() (errors rised)
I try with put() and push() for collections method and agains errors...
How can I add a field in every item of the collection just to pass it to a view?
Try like this, hope it works for you:
$users=User::select('id','name','email','business_name',...)->get()->toArray();
and then use foreach loop like this:
foreach($users as $key => $value ){
$users[$key]['newField'] = "Demo";
}
If you are using Laravel and model in it so there is a better way to add custom attribute or field here is what i do for custom field
For Example :
There is a Model Name User
so in User Model
add a property name appends like :
class User extends Model
{
protected $appends = ['new_field'];
public function getNewFieldAttribute() // defining field logic here
{
return // your code
}
So you no need to use foreach and looping and adding new field
for more have a look on laravel doc : https://laravel.com/docs/5.5/eloquent-mutators#accessors-and-mutators
Suggestion
you can limit your output with Model too.
User::select('id','name','email','business_name',...)->get();
if you are making an array like
User::select('id','name','email','business_name',...)->get()->toArray();
so this will also give you your custom field

Defining an array inside a class in php/laravel

I have a class called shoppingCart and i would like to define an array inside of it. But it seems i need to manually create an instance of an array to use it. I am sure there is a way but i can`t find it
My shopping chart class
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Product;
class shoppingChart extends Model
{
var $products = Array();
var $productCount=0;
public function ItemCount(){
return count($products);
}
}
When i try to use i get a null pointer exceptionn and whenn i checked it $products doesn`t seem to be in variable list.
array_push($sc->products, "test");
I can do it as below. When i am using the array. Works fine.
public function addToBasket(Request $request)
{
$product = new Product();
$product->id = Input::get('product_id');
if($request->session()->has('shoppingCart')){
$sc = $request->session()->get('shoppingCart');
$sc->products = Array(); // If i remove this line code doesn`t work
array_push($sc->products,$product);
$sc->productCount=$sc->itemCount();
}
}
Should i initiate the array everytime i use it ? Doesn`t make any sense to me..
Without knowing more about the architecture of your application i can't go into specifics but I have a feeling that you might not even need this array.
If you are looking at a ShoppingCart has many Product's, then try using an eloquent "Many to Many" relationship using relationships \Illuminate\Database\Eloquent\Relations\HasMany.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
By having this relationship you will be able to get all products related to you cart by going $cart->products which will return an eloquent Collection of the products associated with the cart.

Laravel Eloquent update just if changes have been made

Is there any way to update a record in Laravel using eloquent models just if a change has been made to that record? I don't want any user requesting the database for no good reason over and over, just hitting the button to save changes. I have a javascript function that enables and disables the save button according with whether something has changed in the page, but I would like to know if it's possible to make sure to do this kind of feature on the server side too. I know I can accomplish it by myself (meaning: without appealing to an internal functionality of the framework) just by checking if the record has change, but before doing it that way, I would like to know if Laravel eloquent model already takes care of that, so I don't need to re-invent the wheel.
This is the way I use to update a record:
$product = Product::find($data["id"]);
$product->title = $data["title"];
$product->description = $data["description"];
$product->price = $data["price"];
//etc (string values were previously sanitized for xss attacks)
$product->save();
You're already doing it!
save() will check if something in the model has changed. If it hasn't it won't run a db query.
Here's the relevant part of code in Illuminate\Database\Eloquent\Model#performUpdate:
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = $this->getDirty();
if (count($dirty) > 0)
{
// runs update query
}
return true;
}
The getDirty() method simply compares the current attributes with a copy saved in original when the model is created. This is done in the syncOriginal() method:
public function __construct(array $attributes = array())
{
$this->bootIfNotBooted();
$this->syncOriginal();
$this->fill($attributes);
}
public function syncOriginal()
{
$this->original = $this->attributes;
return $this;
}
If you want to check if the model is dirty just call isDirty():
if($product->isDirty()){
// changes have been made
}
Or if you want to check a certain attribute:
if($product->isDirty('price')){
// price has changed
}
You can use $product->getChanges() on Eloquent model even after persisting. Check docs here
I like to add this method, if you are using an edit form, you can use this code to save the changes in your update(Request $request, $id) function:
$post = Post::find($id);
$post->fill($request->input())->save();
keep in mind that you have to name your inputs with the same column name. The fill() function will do all the work for you :)
use only this:
Product::where('id', $id)->update($request->except(['_token', '_method']));
At times you need to compare the newly changed value with the previous one and if you are looking for that here is the solution.
if (
$obj->isDirty('some_field_name') &&
$obj->some_field_name != $obj->getOriginal('some_field_name')
) {
// Make required changes...
}
});
}
The reference of the derived solution is here.
Maybe Laravel has updated since, but wasChanged is working for me better than isDirty in all of these previous answers.
For example:
if($post->wasChanged('status') && $post->status == 'Ready') // Do thing

Categories