Convert json String into Object of custom class instead of stdClass. - php

my order.php file has
/**
* Encode the cart items from json to object
* #param $value
* #return mixed
*/
public function getCartItemsAttribute($value){
return json_decode($value);
}
And in my controller i fetch cartItems as follows
public function orderDetails(Order $order){
$address = implode(',',array_slice((array)$order->address,2,4));
foreach ($order->cartItems as $item){
dd($item);
}
return view('/admin/pages/productOrders/orderDetails',compact('order','address'));
}
And in above code dd($item) will outputs as follows
{#422 ▼
+"id": 4
+"user_id": 2
+"product_id": 1
+"quantity": 1
+"deleted_at": null
+"created_at": "2018-02-16 08:12:08"
+"updated_at": "2018-02-16 08:12:08"
}
but I want as below.
Cart {#422 ▼
+"id": 4
+"user_id": 2
+"product_id": 1
+"quantity": 1
+"deleted_at": null
+"created_at": "2018-02-16 08:12:08"
+"updated_at": "2018-02-16 08:12:08"
}
How can i achieve this in laravel.

Add true as a second parameter to your decode function like:
/**
* Decode the cart items from json to an associative array.
*
* #param $value
* #return mixed
*/
public function getCartItemsAttribute($value){
return json_decode($value, true);
}
I would create a CartItem model:
// CartItem.php
class CartItem extends Model {
public function order() {
return $this->belongsTo(Order::class);
}
}
Instantiate each one like:
// Controller.php
$cartItems = [];
foreach ($order->cartItems as $item){
// using json_encode and json_decode will give you an associative array of attributes for the model.
$attributes = json_decode(json_encode($item), true);
$cartItems[] = new CartItem($attributes);
// alternatively, use Eloquent's create method
CartItem::create(array_merge($attributes, [
'order_id' => $order->id
]);
}

Related

Symfony : get data from persistent collection in ManyToMany relation

I'm trying to get datas from a persistent collection in a ManyToMany relation.
$form = $this->formRepository->find($id);
dd($form->getAssocies());
give me something like :
Doctrine\ORM\PersistentCollection {#2558
-snapshot: []
-owner: App\Entity\Form {#2617
-id: 174
-name: "Aname"
-category: Proxies\__CG__\App\Entity\Category {#2554
+__isInitialized__: false
-id: 30
-name: null
-children: null
-parent: null
...
when I try this :
$form->getAssocies()->toArray();
it return me an empty array, same result with a ->getValues()
I also tried this :
foreach ($form->getAssocies() as $key => $associe) {
$data['associes'][$key] = $associe;
}
I don't really know why I can't access to theses data
there is my entity :
form.php
/**
* #var Form[]
*
* #ORM\ManyToMany(targetEntity="App\Entity\Form")
*/
private $associes;
public function __construct(){
$this->created = new \DateTime("now");
$this->associes = new ArrayCollection();
}
/**
* #return Form[]
*/
public function getAssocies()
{
return $this->associes;
}
Someone have an idea to how can I get theses datas in an array ?

How paginate result of http response with laravel livewire

I want to populate a table with calling HTTP request and fetch a JSON fine,
I set up live wire like this which can properly populate the table:
public function render()
{
$this->response = Http::timeout(30)->get('http://127.0.0.1:8000/api/lines')->body();
return view('livewire.line-index', ['lines' =>json_decode($this->response)])->layout('layouts.app', ['header' => 'Line Management']);
}
but when I add paginate like this:
public function render()
{
$this->response = Http::timeout(30)->get('http://127.0.0.1:8000/api/lines')->body();
return view('livewire.line-index', ['lines' =>json_decode($this->response)->paginate(25)])->layout('layouts.app', ['header' => 'Line Management']);
}
I see this error:
Call to a member function paginate() on array
Solution:
need to convert array to the collection and then creating a macro for using pagination on
collection.
public function render()
{
$this->response = Http::timeout(30)->get('http://127.0.0.1:8000/api/lines')->body();
$collection = collect(json_decode($this->response));
return view('livewire.line-index', ['lines' =>$collection->paginate(20)])->layout('layouts.app', ['header' => 'Line Management']);
}
For creating a macro you need to update the AppServiceProvider.php file:
<?php
namespace App\Providers;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
/**
* Paginate a standard Laravel Collection.
*
* #param int $perPage
* #param int $total
* #param int $page
* #param string $pageName
* #return array
*/
Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page') {
$page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
return new LengthAwarePaginator(
$this->forPage($page, $perPage),
$total ?: $this->count(),
$perPage,
$page,
[
'path' => LengthAwarePaginator::resolveCurrentPath(),
'pageName' => $pageName,
]
);
});
}
}
Reference: https://gist.github.com/simonhamp/549e8821946e2c40a617c85d2cf5af5e

Laravel model attributes null when i saved it but but I have validated them in the constructor

working with Laravel PHP, I have this model with a constructor where i set the attributes:
class NutritionalPlanRow extends Model
{
use HasFactory;
private $nutritional_plan_id;
private $aliment_id;
private $nomeAlimento;
public function __construct($plan = null,
$aliment = null,
array $attributes = array()) {
parent::__construct($attributes);
if($plan){
$this->nutritional_plan()->associate($plan);
$this->nutritional_plan_id = $plan->id;
}
if($aliment){
$this->aliment()->associate($aliment);
$this->aliment_id = $aliment->id;
$this->nomeAlimento = $aliment->nome;
}
}
/**
* Get the plan that owns the row.
*/
public function nutritional_plan()
{
return $this->belongsTo('App\Models\NutritionalPlan');
}
/**
* Get the aliment record associated with the NutritionalPlanRow.
*/
public function aliment()
{
return $this->belongsTo('App\Models\Aliment');
}
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = [];
/**
* Get the value of nomeAlimento
*/
public function getNomeAlimentoAttribute()
{
return $this->nomeAlimento;
}
/**
* Get the value of plan_id
*/
public function getNutritional_Plan_IdAttribute()
{
return $this->nutritional_plan_id;
}
/**
* Get the value of aliment_id
*/
public function getAliment_IdAttribute()
{
return $this->aliment_id;
}
}
Then I have a controller where I initialize the object:
public function addAlimentToPlan(Request $request){
$planId = $request->planId;
$alimentId = $request->alimentId;
$validatedData = Validator::make($request->all(), [
'planId' => ['required'],
'alimentId' => ['required'],
]);
if ($validatedData->fails()) {
return back()->withErrors($validatedData, 'aliment');
}
$plan = NutritionalPlan::find($planId);
$aliment = Aliment::find($alimentId);
$nutritionalPlanRow = new NutritionalPlanRow($plan, $aliment);
Log::info('Nome Alimento '.$nutritionalPlanRow->getNomeAlimentoAttribute());
$nutritionalPlanRow->save(); //
Toastr::success( 'Alimento aggiunto', '',
["positionClass" => "toast-bottom-right",
"closeButton" => "true"]);
return back();
}
The save operation return this error:
SQLSTATE[23502]: Not null violation: 7 ERRORE: null value in column "nomeAlimento" of relation "nutritional_plan_rows"
but logging the $nutritionalPlanRow->getNomeAlimentoAttribute() the attribure is enhanced.
Someone can help me?
Thank you.
In your constructor you have the following line:
$this->nomeAlimento = $aliment->nome;
You believe that this will fill the attribute in the eloquent model, but that is not happening. Normally such an assignment will pass the magic __set method on the model, but not during model/object construction.
You actually assign it to a property on the object, which is later accessible by your log function, but eloquent doesn't know about it. Therefore it is not sent to the database, resulting in a null error (no default value).
You may use the following to set the values in the constructor:
$this->setAttribute('nomeAlimento', $aliment->nome);
This calls the setAttribute function on the eloquent model, the attribute this becomes part of the model.
(Make sure to change also the other line in your constructor where you assign a value to the object)

Laravel + fractal "deeply" nested includes

I am using fractal (fractal.thephpleague.com) to develop an API with Laravel (laravel.com). It is an amazing library, by the way.
In certain web service, I need to return information of several nested models, which have 3 levels deep. That is, I have a Survey model which has many Survey Items, and each one of them has, in turn, many Survey Item Results (each one of a user). Well, I need the data from all of them, classified, that is:
"surveys": [
{
"id": 1,
...,
"items": [
{
"id": 14,
...,
"results": [
{
"id": 45,
...
},
{
...
}
]
},
{
...
}
]
},
{
...
}
}
With transformers and includes, I get the surveys and survey items info without problems, but I also need the survey item results...
That is, I need something like 2-level "nested" includes, to get the information of the third level.
My best approach, so far (only returning two levels: surveys and survey items). In my controller:
return fractal() -> transform(
Survey::where(...),
new SurveyTransformer()
) -> include(['SurveyItems']) -> respond();
Any help is much appreciated.
Thanks in advance.
Here's what I normally do
Survey Transformer
<?php
namespace App\Transformers;
use League\Fractal;
use App\Survey;
class SurveyTransformer extends Fractal\TransformerAbstract
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'items'
];
public function transform(Survey $survey)
{
return [
'id' => (int) $user->id,
];
}
/**
* Include Items
*
* #param App\Survey $survey
* #return League\Fractal\CollectionResource
*/
public function includeItems(Survey $survey)
{
$items = $survey->items;
if (!is_null($items)) {
return $this->collection($items, new ItemTransformer);
}
return;
}
}
Item Transformer
<?php
namespace App\Transformers;
use League\Fractal;
use App\Item;
class ItemTransformer extends Fractal\TransformerAbstract
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'results'
];
public function transform(Item $item)
{
return [
'id' => (int) $user->id,
];
}
/**
* Include results
*
* #param App\Item $item
* #return League\Fractal\CollectionResource
*/
public function includeResults(Item $item)
{
$results = $item->results;
if (!is_null($results)) {
return $this->collection($results, new ResultTransformer);
}
return;
}
}
On my base controller
/**
* get fractal tranformed data
* #param $resource
*/
protected function fractalResponse($resource, array $includes = [])
{
$manager = new Manager();
$manager->setSerializer(new DataArraySerializer()); //or what ever you like
if (sizeof($includes) == 0) {
$data = $manager->createData($resource)
->toArray();
} else {
$manager->parseIncludes($includes);
$data = $manager->createData($resource)
->toArray();
}
return $data;
}
then
$resource = new \League\Fractal\Resource\Collection($survies, new SurveyTransformer);
$response_data = $this->fractalResponse($resource, ['items.results'])

Laravel: How to Append Properties to an Array

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
}

Categories