Laravel: How to Append Properties to an Array - php

I have an array that gets returned from my show method. This is the show method:
public function show($id)
{
$track = Fuelconsumption::where('id', $id)->first();
return $track;
}
Returns this:
{
id: 6,
distance: 178.6,
volume: 14.31,
price: 1.45,
date: "2015-11-08 14:13:56",
created_at: "2015-11-30 03:29:57",
updated_at: "2015-11-30 03:29:57"
}
I want to make some calculations (averages etc.) from the values provided and append these variables to above json array.
Right now I am solving the problem, by creating a new Kpi object in a new KpiController. The controller passes the above array (which is a FuelConsumption Object) to my constructor.
This is the show method of my KpiController:
public function show($id)
{
$item = Fuelconsumption::where('id', $id)->first();
$kpi = new Kpi($item);
return $kpi['list'];
}
Constructor of my Kpi class:
protected $avgFuel;
protected $avgCost;
protected $cost;
protected $list;
/**
* Creates all necessary KPIs
*
* #param Object $item Fuelconsumption
*/
public function __construct(Fuelconsumption $item)
{
$this->avgFuel = $this->avgFuelperUnitofDistance($item);
$this->avgCost = $this->avgCostPerUnitOfDistance($item);
$this->cost = $this->cost($item);
$this->list = [
'avgFuelperUnitOfDistance' => $this->avgFuel,
'avgCostperUnitOfDistance' => $this->avgCost,
'cost' => $this->cost
];
}
And it returns a Json array that looks like this:
{
avgFuelperUnitOfDistance: 0.08,
avgCostperUnitOfDistance: 0.116,
cost: 20.75
}
The problem I have now is that the first array gets returned when I visit the following URI:
http://localhost:8000/fuelconsumption/6
And the second array gets returned when I visit this URI:
http://localhost:8000/fuelconsumption/6/kpi
The problem is that I want both Json Arrays to be combined in one Array and I am not sure how to accomplish this.
HERE ARE MY THOUGHTS:
Modify Fuelconsumption Class
Modify my FuelConsumptionController to this:
public function show($id)
{
$item = Fuelconsumption::where('id', $id)->first();
$kpi = new Fuelconsumption($item);
dd($kpi);
}
And have a constructor in my Fuelconsumption class:
class Fuelconsumption extends Model
{
protected $fillable = ['distance', 'volume', 'price', 'date'];
protected $dates = ['date'];
protected $cost;
public function __construct($item) {
$this->cost = $this->cost($item);
}
public function cost($item) {
return round($item->volume * $item->price, 2);
}
}
This unfortunately throws the error:
Missing argument 1 for App\Fuelconsumption::__construct()
In my understanding because the class is called even before I recall it in my controller a 2nd time. Not sure how to solve this.
Second Thought: Expand the KPI object
To include all other variables I want to have and then somehow return in my FuelConsumptionController#show method the complete array.
Third Thought: Somehow combine these arrays
Not sure how.
Right now I believe the easiest solution would be to expand on the KPI model (Second Thought of mine), yet I hoped to get rid of the KPI class altogether by somehow passing the item to my FuelConsumption constructor.

Your almost right.. because your class FuelConsumption is an Eloquent model the __construct is already been setup by Laravel and your tried to override it.
What Eloquent does is in case of using ->first() or ->find($id) returning a single model (like what you have). When using ->all() or ->get() it is returning a Eloquent Collection.
Suggestible approach:
class Fuelconsumption extends Model
{
protected $fillable = ['distance', 'volume', 'price', 'date'];
protected $dates = ['date'];
protected $cost;
public function cost() {
return round($this->volume * $this->price, 2);
}
public function avgFuelperUnitofDistance() {
return $this->distance / $volume; // do your thing, dummy calc
}
public function avgCostPerUnitOfDistance() {
return $this->distance / $price; // do your thing, dummy calc
}
}
And your api controller method could look like:
public function show($id)
{
$item = Fuelconsumption::find($id)->first();
// if $item == null if it is item was not found
if (!$item) {
return response('item was missing', 404);
}
// $item will look like:
// class Fuelconsumption: {
// id: 6,
// distance: 178.6,
// volume: 14.31,
// price: 1.45,
// date: "2015-11-08 14:13:56",
// created_at: "2015-11-30 03:29:57",
// updated_at: "2015-11-30 03:29:57"
// }
// doing more stuff over here
// create the json response
return response()->json([
'id' => $item->id,
'distance' => $item->distance,
'volume' => $item->volume,
'price' => $item->price,
'date' => $item->date,
'cost' => $item->cost(),
'avg_fuel' => $item->avgFuelperUnitofDistance(),
'avg_unit' => $item->avgCostperUnitofDistance(),
]);
}
or if you really want to create and merge the attributes:
public function show($id)
{
$item = Fuelconsumption::find($id)->first();
.....
$extra = [
'cost' => $item->cost(),
'avg_fuel' => $item->avgFuelperUnitofDistance(),
'avg_unit' => $item->avgCostperUnitofDistance(),
];
return array_merge($item->getAttributes(), $extra);
}

Another way to solve it would be the suggestion by user naneri with this link:
http://laravel.com/docs/5.1/eloquent-serialization#appending-values-to-json
Then my model would look like this:
class Fuelconsumption extends Model
{
protected $fillable = ['distance', 'volume', 'price', 'date'];
protected $dates = ['date'];
protected $appends = ['cost', 'avg_fuel_per_unit_of_distance', 'avg_cost_per_unit_of_distance'];
public function getCostAttribute()
{
return round($this->attributes['volume'] * $this->attributes['price'], 2);
}
public function getAvgFuelPerUnitOfDistanceAttribute()
{
return round($this->attributes['volume'] / $this->attributes['distance'], 3 );
}
public function getAvgCostPerUnitOfDistanceAttribute()
{
return round($this->attributes['volume'] * $this->attributes['price'] / $this->attributes['distance'], 3);
}
}
And the output of my show method would look like this when getting the URI http://localhost:8000/fuelconsumption/6
{
id: 6,
distance: 178.6,
volume: 14.31,
price: 1.45,
date: "2015-11-08 14:13:56",
created_at: "2015-11-30 03:29:57",
updated_at: "2015-11-30 03:29:57",
cost: 20.75,
avg_fuel_per_unit_of_distance: 0.08,
avg_cost_per_unit_of_distance: 0.116
}

Related

Laravel combine resource collection when returning response

I have a resource which looks like so;
class TestingResource extends JsonResource
{
public function toArray($request)
{
return [
'first' => AnotherResource::collection($this->first),
'second' => AnotherResource::collection($this->second),
];
}
}
What I want to do is combine the 2 so I only have to return one element like so;
class TestingResource extends JsonResource
{
public function toArray($request)
{
return [
'first' => AnotherResource::collection($this->combined),
];
}
}
I tried doing array_merge($this->first, $this->second) but it doesnt work.
Is there any way of getting this to work?
You can use the concat() for collections like:
public function toArray($request)
{
$first = FirstResource::collection(First::all());
$second = SecondResource::collection(Second::all());
$combined = new Collection();
return $combined->concat($first)->concat($second);
}
This concatenates key. The merge() will overwrite the values.

Laravel find() does not get data from the DB

I have a resource Controller with this index method like this:
public function index()
{
$args = [];
$args = array_merge($args, $this->data_creator(35, 12, 'book'));
$args = array_merge($args, $this->data_creator(37, 12, 'kit'));
$args = array_merge($args, $this->data_creator(38, 12, 'game'));
$args['menu_links'] = [
'books' => route('shopping-products.category', Category::find(25)->slug),
'videos' => route('shopping-products.category', Category::find(24)->slug),
'kits' => route('shopping-products.category', Category::find(23)->slug),
'games' => route('shopping-products.category', Category::find(22)->slug),
];
return view('frontend.shop.products.index', $args);
}
But it returns this error:
Trying to get property 'slug' of non-object
And when I dd(Category::find(25), Category::find(24), Category::find(23), Category::find(22)); I get NULL results.
Meaning that it can not find data with specified ids.
However there are 25 records stored at the categories table:
So what is going wrong here? How can I fix this issue?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
Here is Category.php Model:
class Category extends Model
{
use Sluggable, SoftDeletes;
protected $table = 'categories';
protected $primaryKey = 'cat_id';
protected $guarded = [];
/**
* Return the sluggable configuration array for this model.
*
* #return array
*/
public function sluggable()
{
return [
'slug' => [
'source' => 'cat_name'
]
];
}
public function path()
{
return "/products/categories/$this->slug";
}
public function children()
{
return $this->hasMany(Category::class, 'cat_parent_id', 'cat_id');
}
public function parents()
{
return $this->hasMany(Category::class, 'cat_id', 'cat_parent_id');
}
public function products()
{
return $this->belongsToMany(Product::class, 'category_products', 'ctp_cat_id', 'ctp_prd_id');
}
public function news()
{
return $this->belongsToMany(News::class, 'category_news', 'ctn_cat_id', 'ctn_nws_id');
}
public function galleries()
{
return $this->belongsToMany(Gallery::class, 'category_galleries', 'ctg_cat_id', 'ctg_gly_id');
}
public function uploaded()
{
return $this->hasMany(UploadedFile::class, 'upf_object_id', 'cat_id')->where('upf_object_type_id', '=', '107');
}
public function articles()
{
return $this->belongsToMany(Article::class, 'article_category', 'act_cat_id', 'act_art_id');
}
public function olympiadExam()
{
return $this->belongsToMany(OlympiadExam::class, 'olympiads_exams_categories', 'oec_ole_id', 'oec_cat_id');
}
public function olympiadExamQuestion()
{
return $this->belongsToMany(OlympiadExamQuestion::class, 'olympiads_exams_questions_categories', 'oes_cat_id', 'oes_oeq_id')->orderBy('oeq_number', 'asc');
}
public function attr_attributes()
{
return $this->hasMany(CategoryAttribute::class, 'category_id', 'cat_id');
} //
public function attr_product()
{
return $this->hasMany(Product::class, 'prd_cat_att_id', 'cat_id');
} //
public function couponRelation()
{
return $this->hasMany(couponRelation::class, 'object_id', 'cat_id')->where('object_type', 'product_category');
}
public function magazines()
{
return $this->belongsToMany(Magazine::class, 'category_magazine', 'category_id', 'magazine_id');
}
}
And when I do: dd(Category::where('cat_id', 25), Category::where('cat_id', 24), Category::where('cat_id', 23), Category::where('cat_id', 22)); I get this as result:
The problem is because you are using SoftDeletes so soft deleted models will automatically be excluded from query results. In your case, look like Category with id 22, 23, 24, 25 are soft deleted. To get it, you need to use withTrashed() as mentioned in the doc. For example:
Category::withTrashed()->find(22)->slug
per an answer above: if you are using soft deletes you need to add
Category::withTrashed()
However, you can wrap the command in an optional() helper function.
optional(Category::find(22))->slug
// if you are using soft delete
optional( Category::withTrashed()->find(22) )->slug
this will return null if 22 does not exist instead of throwing an exception error.

How to create a new column when exporting a table. Laravel maatwebsite / excel

Please tell me, how create a new column when exporting(to exel) table. There is a table in DB of this kind:
I installed package for export maatwebsite / excel. There is also a my file of model:
class ScheduledInspectionModel extends Model
{
protected $table = 'scheduled_inspection'; // table name
protected $fillable = ['name_smp', 'name_control', "verification_start", "verification_end", 'verification_duration'];
public $timestamps = false;
}
Controller:
class OrganizationsExportController extends Controller
{
public function export()
{
return (new OrganizationsExport)->download('organizations_export.xls');
}
}
And file with export description:
class OrganizationsExport implements FromCollection, ShouldAutoSize, WithHeadings, WithEvents
{
use Exportable;
/**
* #return \Illuminate\Support\Collection
*/
public function collection()
{
return ScheduledInspectionModel::all();
}
public function headings(): array
{
return [
'id',
'Name SMP',
'Name Control',
'Verification Start',
'Verification End',
'Verification Duration'
];
}
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
$event->sheet->getStyle('A1:F1')->applyFromArray([
'font' => [
'bold' => true
]
]);
}
];
}
}
The exported table looks like this:
The export works :) But I want to create in place of the 'id' column (I can exclude it using map ()), 'Number' column and enter the line numbering accordingly. Please tell me how to do this?
I would use the map() function on the export, here you can tweak the source of each column. I assumed column names in your db, due to not knowing the structure. Add one to the count each time it is transformed and you should be golden, which is done with the ++ operator.
private $count = 0;
public function map(ScheduledInspectionModel $inspection): array
{
return [
++$this->count,
$inspection->name_smp,
$inspection->name_control,
$inspection->verification_start->format('Y-m-d') . ' - ' .$inspection->verification_end->format('Y-m-d'),
$inspection->duration,
];
}
To call format on dates, you have to set the dates array on your model.
class ScheduledInspectionModel {
protected $dates = [
'verification_start',
'verification_end',
];
}

Laravel get data from db array not working

When I run the code I get no error but the data I am trying to display is not displaying it's just blank.. can someone tell me what I'm doing wrong?
My controller:
public function openingPage($id) {
$this->getGames();
$games = $this->getGames();
return view('caseopener')->with('games',$games);
}
private function getGames() {
$games = array();
foreach ($this->data->items as $item) {
$game = new Game($item);
$games[] = array(
'id' => $game['id'],
'name' => $game['name'],
'price' => $game['price'],
'image' => $game['image'],
);
}
return $games;
}
The 'Game' Model that is used in 'getGames function':
class Game extends Model
{
private $id;
public $data;
public function __construct($id) {
parent::__construct();
$this->id = $id;
$this->data = $this->getData();
}
private function getData() {
$game = DB::table('products')->where('id', 1)->first();
if(empty($game)) return array();
return $game;
}
}
The view:
#foreach ($games as $game)
<div class="gold">$ {{ $game['price'] }}</div>
#endforeach
I think you are over-complicating things. You could simplify your flow like this:
Given your provided code, it seems like you are using a custom table name ('products') in your Game model. So we'll address this first:
Game.php
class Game extends Model
{
protected $table = 'products'; //
}
Now, it seems like you're searching an array of Game ids ($this->data->items). If so, you could make use of Eloquent for your query, specially the whereIn() method:
YourController.php
public function openingPage($id)
{
$games = Game::whereIn('id', $this->data->items)->get();
return view('caseopener')->with('games', $games);
}
Optionally, if you want to make sure of just returning the id, name, price and image of each Game/product, you could format the response with API Resources:
php artisan make:resource GameResource
Then in your newly created class:
app/Http/Resources/GameResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GameResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'image' => $this->image,
];
}
}
So now just update your controller:
YourController.php
use App\Http\Resources\GameResource;
public function openingPage($id)
{
$games = Game::whereIn('id', $this->data->items)->get();
return view('caseopener')->with('games', GameResource::collection($games));
} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Laravel 5: Having trouble updating related model values by using save/push methods, they just don't work

I'm trying to update some values of a related model but after assigning the new values and using save() or push() the values are not updated in database. More than that, execution just stops at those methods and all I can see is a blank page. No errors, no nothing, it just doesn't even reach the return statement. If I use the try-catch, it just skips the save() or push().
Here is the Product model (just without the fields and methods that are not related to what I'm currently trying to do):
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $table = "products";
public $timestamps = false;
public $fillable = [
...
];
public function userProduct()
{
return $this->belongsTo("\\App\\Models\\UserProduct", "id", "product_id");
}
}
The UserProduct model with fields which I'm trying to update:
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserProduct extends Model
{
protected $primaryKey = null;
public $incrementing = false;
protected $table = "user_product";
public $fillable = [
...
"is_featured",
"is_hidden_from_latest"
...
];
public function product()
{
return $this->belongsTo("\\App\\Models\\Product", "product_id", "id");
}
public function pendingProduct()
{
return $this->belongsTo("\\App\\Models\\PendingProduct", "target_product_id", "target_product");
}
}
Code from the controller:
$replaced_product_sku = Input::get("replaced_sku");
$new_product_sku = Input::get("new_sku");
$products = Product::with([
"userProduct" => function($q) {
$q->orderBy("updated_at", "asc");
}
])->where("product_status", "live")->get();
if (!$found_replaced = $products->where("item_sku", $replaced_product_sku)->first()) {
return redirect("admin/content")
->with("danger", "Replaced product was not found.");
}
if (!$found_new = $products->where("item_sku", $new_product_sku)->first()) {
return redirect("admin/content")
->with("danger", "The new featured product was not found.");
}
$found_replaced->userProduct->is_featured = 0;
$found_replaced->userProduct->is_hidden_from_latest = 1;
$found_new->userProduct->is_featured = 1;
$found_new->userProduct->is_hidden_from_latest = 0;
$found_replaced->userProduct->save();
$found_new->userProduct->save();
return redirect("admin/content")
->with("...", "...");
Tried using push() method instead of save() but the only thing that happens is that execution stops at $found_replaced->userProduct->save(); and a blank page is displayed. Also tried something like this:
$found_replaced->update([
"userProduct.is_featured" => 0,
"userProduct.is_hidden_from_latest" => 1
]);
$found_new->update([
"userProduct.is_featured" => 1,
"userProduct.is_hidden_from_latest" => 0
]);
but still without success.
First you have to fix the relations:
In Product model:
public function userProduct()
{
return $this->hasOne("\\App\\Models\\UserProduct", "product_id", "id");
}
In UserProduct model:
public function product()
{
return $this->belongsTo("\\App\\Models\\Product", "product_id", "id");
}
The solution was using this approach:
$found_replaced->update([
"userProduct.is_featured" => 0,
"userProduct.is_hidden_from_latest" => 1
]);
$found_new->update([
"userProduct.is_featured" => 1,
"userProduct.is_hidden_from_latest" => 0
]);
that I've posted in the question, but the mistake was that I was using it wrong, so I've edited it into this and it worked fine:
$found_replaced->userProduct()->update([
"is_featured" => 0,
"is_hidden_from_latest" => 1
]);
$found_new->userProduct()->update([
"is_featured" => 1,
"is_hidden_from_latest" => 0
]);
Seems that save() just doesn't work as expected on relation attributes.
Thank you for your help anyway! ;)

Categories