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.
Related
I want to decrease quantity from products table when order is added to cart ,right now when I add something to cart, correct quantity is not decreasing. For example if a I place order of 6 items then a random amount gets decreased from my products table.
Here is my code in order controller of livewire:
public function IncrementQty($cartId)
{
$carts=Cart::find($cartId);
$carts->increment('product_qty',1);
$updatePrice=$carts->product_qty * $carts->product->price;
$carts->update(['product_price'=>$updatePrice]);
$getProductStock=Product::where(['id'=>$carts['product_id']])->first()->toArray();
$newStock=$getProductStock['quantity'] - $carts['product_qty'];
Product::where(['id'=>$carts['product_id']])->update(['quantity'=>$newStock]);
$this->mount();
}
Being a beginner I am unable to understand about what's wrong here. Should I be substracting it from orderDetails table instead of carts table? If yes then how?
A couple of things to note first,
You should not call $this->mount() or any other lifecycle hooks from Livewire. If there are code inside your mount-method you need to run again, then abstract it into a separate method, and call it from both your mount() method and your IncrementQty() method.
You can use model-route-binding to inject the correct model into the method
public function IncrementQty(Cart $cart)
You can then simplify your code by using the product relation on the cart directly, and calling decrement() on that.
public function IncrementQty(Cart $cart)
{
$product = $cart->product;
$cart->increment('product_qty', 1);
$cart->update(['product_price' => $cart->product_qty * $product->price]);
$product->decrement('quantity', 1);
$this->mount(); // Do not call mount manually, abstract the contents you need into a separate method instead
}
If you are still experiencing issues, then it cannot possibly be this piece of code, or you are misreading your data somehow - in any case, if you are still experiencing issues, you will need to provide additional information with your table-structure, models, how you call this method, and data before and after your actions.
it seems you have a problem in this line:
$newStock=$getProductStock['quantity'] - $carts['product_qty'];
$carts is an object not an array, so, do:
$newStock = $getProductStock['quantity'] - $carts->product_qty;
really need your help here. ( I don't know what I want is possible on Eloquent )
Lets pretend this Relationship: One user can have many Childs
Note: Ignore problems in the code, this is just an example.
Now lets add some code into it.
// Return HasMany Object Instance from Eloquent.
$hasMany = $user->childs()
// Perform Mass Update.
$hasMany->update(['born_at' => Carbon::now])
So far nothing wrong with it, the first line returns an HasMany Object ( Documentation )
The problem is that Mass Updating touches my Model's timestamps ( created_at, updated_at ) and specially for this update I don't want it to do that.
Disabling it on the Model is not an option for me I do use the timestamp touch normally but I don't want to use in this case.
Neither I want to iterate over the Collection ( $user->childs ) because I have many rows to update and its an overhead to generate one query for each Model to update.
What I expect for an answer to this question: Simple, I just want an way to turn off the timestamps to do the mass updating or something like that.
( Normally on a single Model you can disable it like this: $model->timestamps = false, but this will not work here because hasMany instance does not have this attribute. )
You could set the property default of the model to false. So in you're model class you will have:
public $timestamps = false;
But this will always disable the timestamps until je use:
$model->timestamps = true;
In case someone finds this through Google:
One possible solution is to Fallback to the base QueryBuilder:
(new Child)
->newQuery()
->toBase()
->where('parent_id', $model->id)
->update([
'born_at' => Carbon::now,
]);
Of course, one could just use here something like DB::table(...)...
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.
I'm working in Laravel 4, and I have a Child model with multiple EducationProfiles:
class Child extends EloquentVersioned
{
public function educationProfiles()
{
return $this->hasMany('EducationProfile');
}
}
If I wanted to get all the EducationProfiles for each kid under age 10 it would be easy:
Child::where('date_of_birth','>','2004-03-27')->with('educationProfiles')->all();
But say (as I do) that I would like to use with() to grab a calculated value for the Education Profiles of each of those kids, something like:
SELECT `education_profiles`.`child_id`, GROUP_CONCAT(`education_profiles`.`district`) as `district_list`
In theory with() only works with relationships, so do I have any options for associating the district_list fields to my Child models?
EDIT: Actually, I was wondering whether with('educationProfiles') generates SQL equivalent to:
EducationProfile::whereIn('id',array(1,2,3,4))
or whether it's actually equivalent to
DB::table('education_profiles')->whereIn('id',array(1,2,3,4))
The reason I ask is that in the former I'm getting models, if it's the latter I'm getting unmodeled data, and thus I can probably mess it up as much as I want. I assume with() generates an additional set models, though. Anybody care to correct or confirm?
Ok, I think I've cracked this nut. No, it is NOT possible to eager load arbitrary queries. However, the tools have been provided by the Fluent query builder to make it relatively easy to replicate eager loading manually.
First, we leverage the original query:
$query = Child::where('date_of_birth','>','2004-03-27')->with('educationProfiles');
$children = $query->get();
$eagerIds = $query->lists('id');
Next, use the $eagerIds to filterDB::table('education_profile') in the same way that with('educationProfiles') would filter EducationProfile::...
$query2 = DB::table('education_profile')->whereIn('child_id',$eagerIds)->select('child_id', 'GROUP_CONCAT(`education_profiles`.`district`) as `district_list`')->groupBy('child_id');
$educationProfiles = $query2->lists('district_list','child_id');
Now we can iterate through $children and just look up the $educationProfiles[$children->id] values for each entry.
Ok, yes, it's an obvious construction, but I haven't seen it laid out explicitly anywhere before as a means of eager loading arbitrary calculations.
You can add a where clause to your hasMany() call like this:
public function educationProfilesUnderTen() {
$ten_years_ago = (new DateTime('10 years ago'))->format('Y-m-d');
return $this->hasMany('EducationProfile')->where('date_of_birth', '>', $ten_years_ago)
}
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.