I am working on a back-end that needs to re-calculate the price based on user-specific currency.
I fetch all required records from the database as following:
$tasks = Task::with([
'images' => function($query){},
'price' => function($query){},
'user' => function($query){ $query->with('images');},
])->whereDoesntHave('tasksState', function($query) use ($user){
$query->where('user_id', $user->id);
$query->where('state', '<>', 0);
})->where('id', '>', $offset)->where('user_id', '<>', $user->id)->take($limit)->get();
Task's price model consists of currency and value attributes.
Then I iterate through $tasks and re-calculate the price based on the user-specific currency:
foreach ($tasks as $k => $task){
$price = $task->price->value;
$price = $price * $rate->getValue();
$tasks[$k]->price = $price;
//$task->price = $price;
}
After inspecting the tasks, all price are re-calculated correctly.
However when I subsequently serialize $tasks and send it as a response, it contains the data as they were fetched from the database - no re-calculated prices.
Does anybody where is the problem? Any ideas will be greatly appreciated!
Since you're serializing the data anyway, you can first convert collection into an array and work it as with an array:
$tasks->toArray();
In this case you'll be able to override original data as you've tried to accomplish with foreach loop.
As alternative, you could create a mutator which will add calculated_price property to the collection.
You can use Eloquent Mutator for this.
Create following function in Task Model
public function getNewPriceAttribute($value)
{
...// do your processing here
}
And then you can use it as:
$task->new_price;
After creating the accessor, add the attribute name to the appends property on the Task model.
protected $appends = ['new_price'];
Once the attribute has been added to the appends list, it will be included in both the model's array and JSON representations.
Related
I have a get formule that returns some nested relationships in an array. I was wondering how to access them in a where statement.
The initial get
$taken = UserWork::with('work.place')
->with('user')
->with('work.timeslot')
->get();
I tried something like this
$taak = $taken->where('work.timeslot[0].start_hour',"17:00:00")->first();
json result from $taken
Using with will endup with two queries. if you want to bring the user with timeslot null then there no need to add whereHas
$callback = function($query) {
$query->where('start_hour',"17:00:00");
};
$taken = UserWork::whereHas('work.timeslot', $callback)
->with(
['work.place', 'user', 'work.timeslot' => $callback]
)->get();
I am trying to do something I've never done before in Laravel and cannot figure out how to do it.
I have the following code in my Controller:
public function show($id)
{
//Get application for drug
$application = PharmaApplication::where('ApplNo', $id)->first();
//Get all products for given application (i.e. the different quantities and forms drug comes in)
$product = PharmaProduct::where('ApplNo', $id)->get();
foreach($product as $product){
$product->ProductNo;
}
//Get Marketing Status for drug
$marketingStatus = DB::table('pharma_marketing_statuses')
->where('ApplNo', $id)
->where('ProductNo', $product->ProductNo)
->get();
//Lookup marketing status Description
$marketingStatusDescription = PharmaMarketingSatusLookup::where('MarketingStatusID', $marketingStatus->MarketingStatusID);
return view('profiles.drug', compact('application', 'product', 'marketingStatus', 'marketingStatusDescription'));
}
I am trying to accomplish the following:
Get the application for a drug - this part of my code works
Return an array of objects for the products (i.e. 7 products that belong to one application). I can do this but get stuck going to the next part.
Next, I have to use the array of objects and search a table with the following columns: MarketingStatusID, ApplNo, ProductNo. I know how to query this table and get one row, but the problem is I have an array that I need to search. I imagine I have to use a loop but don't know where.
Finally, I use the MarketingStatusID to retrieve the MarketingStatusDescription which I will know how to do.
I am also getting an error message that says:
Class 'App\Http\Controllers\profiles\PharmaMarketingSatusLookup' not found
In my Controller, I have use App\PharmaMarketingStatusLookup; so I am not sure why it is searching the Controllers folder
You have a typo in your class
From PharmaMarketingSatusLookup change to PharmaMarketingStatusLookup
App\Http\Controllers\profiles\PharmaMarketingStatusLookup
USE whereIn
use App\PharmaApplication;
use App\PharmaProduct;
use App\PharmaMarketingSatusLookup;
public function show($id)
{
$application = PharmaApplication::where('ApplNo', $id)->first();
$products = PharmaProduct::where('ApplNo', $id)->get();
$productid = array();
foreach($products as $product){
$productid[] = $product->ProductNo;
}
$marketingStatus = DB::table('pharma_marketing_statuses')
->where('ApplNo', $id)
->whereIn('ProductNo', $productid)
->get();
$marketingStatusDescription = PharmaMarketingSatusLookup::where('MarketingStatusID', $marketingStatus->MarketingStatusID);
return view('profiles.drug', compact('application', 'product', 'marketingStatus', 'marketingStatusDescription'));
}
I have an API returning an object with one-to-one relation to another object. As the models behind the objects, do have timestamps, these are also delivered when asking API.
// Get all transactions
Route::get('transaction', function() {
return Transaction::with('Personone','Persontwo')->get();
});
How do I prevent Laravel from returning the timestamps of the objects in the API?
I googled, but only found some hints to middleware or response macros but found no example pointing me into the right direction. Maybe you can help me.
You can make attributes "hidden" so that they do not show up in json.
docs
class Transaction extends Model
{
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = ['timestamp'];
}
I'm not sure if I get the question correctly but if you want to select from eager loads there are two ways
first one is inline selecting
Route::get('transaction', function () {
return Transaction::with('Personone:id,foo,bar', 'Persontwo:id,foo,bar,foobar')->get();
});
second one is to pass a closure
Route::get('transaction', function () {
return Transaction::with([
'Personone' => function ($query) {
$query->select('id', 'foo', 'bar');
},
'Persontwo' => function ($query) {
$query->select('id', 'foo', 'bar', 'foobar');
}])->get();
});
Eager Loading Specific Columns You may not always need every column
from the relationships you are retrieving. For this reason, Eloquent
allows you to specify which columns of the relationship you would like
to retrieve:
$users = App\Book::with('author:id,name')->get();
Constraining Eager Loads Sometimes you may wish to eager load a
relationship, but also specify additional query constraints for the
eager loading query. Here's an example:
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%'); }])->get(); In this example, Eloquent will only eager load posts where the post's title
column contains the word first. Of course, you may call other query
builder methods to further customize the eager loading operation:
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc'); }])->get();
I have a CMS that allows the user to save and create bike tours. Each bike tour also has categories, which are definined using Laravel's Many to Many relationship utilising an intermediary pivot table. At the point of saving a tour, we don't know if the tour is an existing one being edited, or a new one.
I think I should be using Laravel's firstOrNew method for saving the tour, and the sync method for saving categories. However, all the tutorials very simplistically just give the example of passing a single object to the function like so:
$tour = Tour::firstOrNew($attributes);
But what happens when my $attributes also contains extra stuff, like the categories which are linked to a relationship table, and which I will need to save in the next step? For example this very good tutorial gives the following example:
$categories = [7, 12, 52, 77];
$tour = Tour::find(2);
$tour->categories()->sync($categories);
But what happens if the category data is bundled with the data for the rest of the tour, and instead of using find I need to use firstOrNew to create the tour? Should I keep the categories in the $attributes while I instantiate the tour, then run the sync, then unset them before saving the tour, or...? Is there a better way to achieve this?
EDIT: To be clear, the $attributes variable in my example here is essentially the tour object data bundled together- just as the Laravel/Eloquent system would return it from the transaction using the belongsToMany method- with subequent modifications from the user). ie: here is a snapshot of what it contains:
array (
'id' => 1,
'uid' => '03ecc797-f47e-493a-a85d-b5c3eb4b9247',
'active' => 1,
'code' => '2-0',
'title' => 'Tour Title',
'url_title' => 'tour_title',
'distance_from' => 20,
'distance_to' => 45,
'price_from' => '135.00',
'price_to' => '425.00',
'created_at' => '2013-12-31 15:23:19',
'updated_at' => '2015-07-24 16:02:50',
'cats' => // This is not a column name!
array (
0 => 1,
1 => 7
),
)
All of these attributes are column names in my tours table, other than cats, which references another table via a hasMany relationship. Do I need to unset it manually before I can set this object class and save it with $tour->save?
I am looking for the cleanest most Laravel way to do it?
EDIT2: Here is the relationship defined in the Tours model:
class Tour extends Model
{
protected $guarded = [];
public function cats(){
return $this->belongsToMany('App\TourCategory', 'tour_cat_assignments', 'tour_id', 'cat_id');
}
}
you need to define $fillable property of your Tour model to tell eloquent which attributes to consider when using mass assignment so it will ignore categories related attributes silently. for ex.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tour extends Model {
protected $fillable = ['name'] //... other attributes which are part of this model only and laravel will consider only these attributes and ignore category related attributes which you can consider later use.
}
You can use firstOrCreate. The data actually gets persisted using this method.
$categories = [7, 12, 52, 77];
$tour = Tour::firstOrCreate($attributes)->cats()->sync($categories);
Got to make sure the fields are mass-assignable to be able to use the firstOrCreate method though. So either set the fieldnames in the $fillable property or put this in the Tour model:
protected $guarded = [];
Since you have mentioned "CMS" and "subsequent modifications from user", I guess that you are getting your attributes from a Form which means you are getting a Request object/collection.
If that is the case then you can try
$tour = Tour::firstOrCreate($request->except('cats'));
$categories = [];
foreach($request->get('cats') as $key=>$value){
$categories[] = $value;
}
$tour->cats()->sync($categories);
However, if your $attributes us constructed as an array (probably with some manipulations on form data) as per your EDIT then in that case you may try:
$tour = Tour::firstOrCreate(array_except($attributes, ['cats']);
$categories = [];
foreach($attributes['cats'] as $key=>$value){
$categories[] = $value;
}
$tour->cats()->sync($categories);
In any case, you must have the mass assignable fields declared in $fillable property in your model i.e. Tour.
Hope this helps.
I know that we can insert array in Session as Session::push('person.name','torres') but how to keep laravel collection object such as $product = Product::all();,as like Session::put('product',$product);.Ho wto achieve this?
You can put any data inside the session, including objects. Seeing as a Collection is just an object, you can do the same.
For example:
$products = Product::all();
Session::put('products', $products);
dd(Session::get('products'));
Should echo out the collection.
You should convert it to a plain array, then convert them back to models:
Storing in the session
$products = Product::all()->map(function ($product) {
return $product->getAttributes();
})->all();
Session::put('products', $products);
Retrieving from the session
$products = Product::hydrate(Session::get('products'));
You can see an example of this method here.