Undefined property: stdClass::$total - php

I am working on adding the product values into the cart and i am using darryldecode Laravel shopping cart.
When I try to get the image and total price from the attribute array, I get the error Undefined property: stdClass::$total.
Here is my Controller:
public function cart(Request $request , $id)
{
// return $request;
$cart = Cart::add([
'id' => $request->id,
"name" => $request->name,
"crm" => $request->sku,
"quantity" => $request->qty,
"price" => $request->price,
"attributes" => array(["image" => $request->image] , "total" => $request->price * $request->qty)
]);
if($cart)
{
return redirect()->route('cart');
}
}
Here is the Cart Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Cart;
class CartController extends Controller
{
public function index()
{
// return Cart::getContent();
return View('demo', [ 'cart' => Cart::getContent()]);
}
}
Here is the view where I try to print the image and total
#foreach($cart as $product)
Name:<td>{{ $product->name}}</td>
Price:<td>{{ $product->price}}</td>
Quantity:<td>{{ $product->qty}}</td>
Attributes:<td>{{ $product->attributes}}</td>
#foreach(json_decode($product->attributes) as $details)
Image:<td>{{ $details->image}}</td>
Total:<td>{{ $details->total}}</td>
{{ $details}}
#endforeach
#endforeach

You have a typo in this line:
"attributes" => array(["image" => $request->image] , "total" => $request->price * $request->qty)
Rather do this:
"attributes" => ["image" => $request->image, "total" => $request->price * $request->qty]

You have to add both attributes into one array when you want get into view (blade) file.
Use below code:
$cart = Cart::add([
'id' => $request->id,
"name" => $request->name,
"crm" => $request->sku,
"quantity" => $request->qty,
"price" => $request->price,
"attributes" => array(["image" => $request->image, "total" => $request->price * $request->qty])
]);

Related

Laravel: Get the correct value of an enum field on a resource class

I'm using PHP8.1 and Laravel 9 for a project in which I've got the following enum:
enum OrderStatuses : string
{
case New = 'new';
case Pending = 'pending';
case Canceled = 'canceled';
case Paid = 'paid';
case PaymentFailed = 'payment-failed';
public function createOrderStatus(Order $order) : OrderStatus
{
return match($this) {
OrderStatuses::Pending => new PendingOrderStatus($order),
OrderStatuses::Canceled => new CanceledOrderStatus($order),
OrderStatuses::Paid => new PaidOrderStatus($order),
OrderStatuses::PaymentFailed => new PaymentFailedOrderStatus($order),
default => new NewOrderStatus($order)
};
}
one of the classes listed in the enum looks like this:
abstract class OrderStatus
{
public function __construct(protected Order $order)
{
}
/**
* Determines whether an order can transition from one status into another
*
* #return bool
*/
abstract public function canBeChanged() : bool;
}
class PaidOrderStatus extends OrderStatus
{
public function canBeChanged(): bool
{
return false;
}
}
all others are basically the same, they just differ on the implementation of the canBeChanged method.
Now, I've got the following resource:
class OrdersResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'orders',
'attributes' => [
'status' => $this->status,
'payment_type' => $this->payment_type,
'payment_transaction_no' => $this->payment_transaction_no,
'subtotal' => $this->subtotal,
'taxes' => $this->taxes,
'total' => $this->total,
'items' => OrderItemsResource::collection($this->whenLoaded('orderItems')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
}
which is called from my controller like this:
return (new OrdersResource($order))
->response()->setStatusCode(ResponseAlias::HTTP_OK);
Before implementing the enum my resource was working correctly, it returned the expected data. But after the enum, it's returning [] for the status field.
A sample return is currently looking like this:
"id" => "86b4e2da-76d4-4e66-8016-88a251513050"
"type" => "orders"
"attributes" => array:8 [
"status" => []
"payment_type" => "card"
"payment_transaction_no" => "3kaL92f5UwOG"
"subtotal" => 3005.76
"taxes" => 0
"total" => 3005.76
"created_at" => "2022-08-31T12:47:55.000000Z"
"updated_at" => "2022-08-31T12:47:55.000000Z"
]
]
notice again the value for status.
I've got a casting and a attribute in my Order's model:
protected $casts = [
'status' => OrderStatuses::class,
];
protected function status(): Attribute
{
return new Attribute(
get: fn(string $value) =>
OrderStatuses::from($value)->createOrderStatus($this),
);
}
Furthermore, if I dd the type of $this->status in the toArray method from OrdersResource it says that it is of type Domain\Order\Enums\PaidOrderStatus which is correct.
I tried adding __toString() to PaidOrderStatus class but had no luck. What am I missing?
Update
I've added a test() method to PaidOrderStatus:
class PaidOrderStatus extends OrderStatus
{
public function canBeChanged(): bool
{
return false;
}
public function test() : string
{
return OrderStatuses::Paid->value;
}
}
and then did:
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'orders',
'attributes' => [
'status' => ($this->status)->test(),
'payment_type' => $this->payment_type,
'payment_transaction_no' => $this->payment_transaction_no,
'subtotal' => $this->subtotal,
'taxes' => $this->taxes,
'total' => $this->total,
'items' => OrderItemsResource::collection($this->whenLoaded('orderItems')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
and that gave me:
[
"id" => "8a2d6024-a63f-44ba-a145-cede2ecf3aaa"
"type" => "orders"
"attributes" => array:8 [
"status" => "paid"
"payment_type" => "card"
"payment_transaction_no" => "kC9upaoGb2Nd"
"subtotal" => 657.26
"taxes" => 0
"total" => 657.26
"created_at" => "2022-08-31T13:17:25.000000Z"
"updated_at" => "2022-08-31T13:17:25.000000Z"
]
and that worked. But it's a very hacky solution and I'd like to do better.
I'm sure you've already solved this as it's been months ago, but first you don't need the Attribute mutator as you have already defined the cast which will correctly map the string value to the right enum case.
Then in the resource, you just get the value from the enum like so.
'status' => $this->status->value,

laravel create wont store first row of data array

I have select options in my form where it has first row when page loads and users can add more rows by AJAX and store all rows data at once.
The problem is that my first row (which is visible when page loads) does not save the data while all other added rows will be saved.
Here is a screenshot of my select options when page loads:
And here it is when user adds new rows:
Here is my sample data that those rows have sent to controller (screenshot #2):
array:2 [▼
0 => array:3 [▼
"name" => "ali"
"username" => "alireza"
"action" => "delete"
]
1 => array:3 [▼
"name" => "eraty"
"username" => "aery"
"action" => "optional"
]
]
As I explained in my screenshots, object 0 data will not be store in the database, not sure why.
Here is my controller:
public function update(Request $request, $id)
{
$this->validate($request, [
'note' => 'required|string',
]);
$post = Post::where('id', $id)
->where('user_id', Auth::id())
->first();
$post->user_id = Auth::id();
$post->note = $request->input('note');
if ($post->save()) {
// social media (add new)
$socialHeir = $request->input('social_media_heir');
$socialAction = $request->input('social_media_action');
$socialNames = $request->input('social_media_name');
$socialUsernames = $request->input('social_media_username');
if (!empty($socialNames)) {
$result_array = [];
foreach ($socialNames as $key => $val) {
$result_array[$key] = [
'name' => $socialNames[$key],
'username' => $socialUsernames[$key],
'action' => $socialAction[$key]
];
}
// dd($result_array); <-- result of this line I shared above (after screenshot 2)
foreach ($result_array as $key => $merge) {
if (!empty($key) && !empty($merge)) {
SocialMedia::create([
'owner_id' => Auth::id(),
'heir_id' => $socialHeir,
'post_id' => $post->id,
'name' => $merge['name'],
'username' => $merge['username'],
'what_to_do' => $merge['action'],
]);
}
}
}
}
return redirect()->route('posts.index');
}
Update
SocialMedia model:
protected $fillable = [
'owner_id',
'heir_id',
'post_id',
'name',
'username',
'what_to_do',
];
public function post()
{
return $this->belongsTo(Post::class);
}
Post model
protected $fillable = [
'user_id',
'note',
];
public function socialMedias()
{
return $this->hasMany(SocialMedia::class);
}
Solved
The issue was in $key in my foreach after removing it, it saves all rows now.
foreach($result_array as $merge) {
if(!empty($merge)) {
SocialMedia::create([
'owner_id' => Auth::id(),
'heir_id' => $socialHeir,
'post_id' => $will->id,
'name' => $merge['name'],
'username' => $merge['username'],
'what_to_do' => $merge['action'],
]);
}
}

How to create a factory for post where it also enters data in meta table for that post in laravel

using factory is a cleaner way to create seed data.
I can generate the result from another method of foreach or for loop. but how to do it with the factory ?
below is the post factory page
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Post;
use App\MetaData;
use Faker\Factory;
$factory->define(Post::class, function () {
$faker = Faker\Factory::create('en_IN');
$w = $faker->unique()->sentence.' '.mt_rand(0,1000);
$r = [
'title' => $w,
'slug' => strtolower(str_replace(' ', '-', $w)),
'banner' => 'https://source.unsplash.com/random/600x600',
'content' => $faker->text,
'views' => mt_rand(0,1000),
'status' => rand(0,1),
'creator_id' => mt_rand(0,100),
'moderator_id' => mt_rand(0,100),
];
$factory->define(MetaData::class, function () {
return [
'for' => 'article',
'record_id' => $r->id,
'title' => $w,
'slug' => strtolower(str_replace(' ', '-', $w)),
'description' => $faker->sentences,
'banner' => 'https://source.unsplash.com/random/600x600',
'keywords' => $faker->words,
'status' => 1,
'creator_id' => mt_rand(0,100),
'moderator_id' => mt_rand(0,100),
];
});
return $r;
});
I want to do something like this, but end up with an error like below:
ErrorException : Undefined variable: factory
at /Users/dragonar/Dev/pdp/database/factories/PostFactory.php:23
19| 'creator_id' => mt_rand(0,100),
20| 'moderator_id' => mt_rand(0,100),
21| ];
22|
> 23| $factory->define(MetaData::class, function () {
24| return [
25| 'for' => 'article',
26| 'record_id' => $r->id,
27| 'title' => $w,
Exception trace:
1 Illuminate\Foundation\Bootstrap\HandleExceptions::handleError("Undefined variable: factory", "/Users/dragonar/Dev/pdp/database/factories/PostFactory.php", [Object(Faker\Generator), "Aut voluptatum sed aut beatae. 380"])
/Users/dragonar/Dev/pdp/database/factories/PostFactory.php:23
2 Illuminate\Database\Eloquent\Factory::{closure}(Object(Faker\Generator), [])
/Users/dragonar/Dev/pdp/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:273
Please use the argument -v to see more details.
if this solves, I also want to add categories and tags to the same post.
please try this
$factory->define('App\MetaData', function($faker) use ($factory) {
// Your stuff here
});
The problem here is that you are trying to define the child inside the factory of the parent.
You are getting an ErrorException : Undefined variable: factory error, that tells you that $factory is not defined. This is because you are doing it in the closure for the post factory.
One way you can get around this, is to reference the post when creating the meta data, so you do not create them both at the same time.
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Post;
use App\MetaData;
use Faker\Factory;
$factory->define(Post::class, function () {
$faker = Faker\Factory::create('en_IN');
$w = $faker->unique()->sentence.' '.mt_rand(0,1000);
return [
'title' => $w,
'slug' => strtolower(str_replace(' ', '-', $w)),
'banner' => 'https://source.unsplash.com/random/600x600',
'content' => $faker->text,
'views' => mt_rand(0,1000),
'status' => rand(0,1),
'creator_id' => mt_rand(0,100),
'moderator_id' => mt_rand(0,100),
];
});
$factory->define(MetaData::class, function () {
return [
'for' => 'article',
'record_id' => factory(Post::class)->id,
'title' => $w,
'slug' => strtolower(str_replace(' ', '-', $w)),
'description' => $faker->sentences,
'banner' => 'https://source.unsplash.com/random/600x600',
'keywords' => $faker->words,
'status' => 1,
'creator_id' => mt_rand(0,100),
'moderator_id' => mt_rand(0,100),
];
});
When doing it like this, you first create the post, then you create the metadata and reference the post when creating it. However, I am not sure that helps you with your issue. So what you could do is create a helper class, where you can pass in some overrides for both classes, in case you want to control what data goes into each model.
Can be done like so:
<?php
use App\Post;
use App\MetaData;
class FactoryHelper {
public static function createPostWithMetaData(array $postAttributes = [], array $metaDataAttributes = [])
{
$post = factory(Post::class)->create(postAttributes);
$metaData = factory(MetaData::class)->create(array_merge([
'record_id' => $post->id
], $metaDataAttributes));
return $post;
}
}

How can I rewrite/reformat an array?

I am developing an API but I am having a problem connecting it to a payment platform, because they ask for the preference with the following format:
'items' => [
[
'id' => 1,
'title' => 'Product 1',
'description' => 'Some description',
'picture_url' => 'img.png',
'quantity' => 1,
'currency_id' => 'USD',
'unit_price' => 20
],
[
'id' => 2,
'title' => 'Product 2',
'description' => 'Some description',
'picture_url' => 'img.png',
'quantity' => 1,
'currency_id' => 'USD',
'unit_price' => 25
]
]
but I am receiving my data from my items in the cart like this:
'items' => json_encode(new CartItemCollection($items))
And that collection (my collection CartItemCollection) have this format:
{
"Items":[
{
"productID":1,
"inventoryID":1,
"name":"Product 1",
"Quantity":1,
"price":20,
"image":"img.png"
},
{
"productID":2,
"inventoryID":1,
"name":"Product2 "
"Quantity":1,
"price":25,
"image":"img.png"
}
],
"items_count":2,
"products_count":2
}
So I'm sending (which is wrong):
'items' => "Items":[
{
"productID":1,
"inventoryID":1,
"name":"Product 1",
"Quantity":1,
"price":20,
"image":"img.png"
},
{
"productID":2,
"inventoryID":1,
"name":"Product2 "
"Quantity":1,
"price":25,
"image":"img.png"
}
],
"items_count":2,
"products_count":2
How can I rewrite or re format that: json_encode(new CartItemCollection($items)) to get the rigth array?
I kind of need to do this:
foreach (Items) in my collection, do: ProductID (mine) rewrite as id (plataform), Quantity (mine) rewrite as quantity (plataform), price (mine) rewrite as unit_price (plataforms) etc etc..
Thank you in advance :)
According to the official documentation, a different approach could be define another Collection Resource class to customise the response.
return new CustomCartItemCollection($items);
Inside the CustomCarItemCollection.php class:
use Illuminate\Http\Resources\Json\ResourceCollection;
class CustomCarItemCollection extends ResourceCollection
{
public $collects = 'App\Http\Resources\Item';
public function toArray($request)
{
return [
'items' => $this->collection,
];
}
}
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Item extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->ProductID,
'quantity' => $this->Quantity,
'unit_price' => $this->Price,
];
}
}
From the code it seems like you are using Eloquent API resources for your CartItem model.
If that's correct you shouldn't use json_encode, as it would convert your object to a string, but you can try to call directly the toArray method on the CartItemCollection:
'items' => (new CartItemCollection($items))->toArray()['Items']
This code might need a bit of tweaking as you didn't post the CartItemCollection's class code as well as other relevant code you use to generate the structure of the output you are now getting.

Cake 3 saving belongsToMany relation crashes

I currently have an belongsToMany relationship between two Table, Skus and Medias. I named the join table skus_images though.
I'm here trying to save only ids, not inserting new data in an HABTM way.
I have in my form :
echo $this->Form->input('images._ids', ['options' => $images, 'multiple' => 'checkbox']);
And everything is working fine there, I'm correctly getting my Medias listed.
But whenever I try to submit the form, I get this :
Error: Call to a member function get() on a non-object
File /home/weshguillaume/AndyToGaby/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php
Line: 874
I've defined my relationship as such in SkusTable :
$this->belongsToMany('Images', [
'className' => 'Media.Medias',
'joinTable' => 'skus_images',
'targetForeignKey' => 'image_id'
]);
The context doesn't give any insights, neither does the stack trace as it's both (almost) empty. Thanks :)
EDIT:
Controller add method:
public function add($product_id)
{
$skus = $this->Skus->newEntity();
if ($this->request->is('post')) {
$skus = $this->Skus->patchEntity($skus, $this->request->data(), [
'associated' => [
'Attributes'
]
]);
if ($this->Skus->save($skus)) {
$this->Flash->success('The skus has been saved.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The skus could not be saved. Please, try again.');
}
}
$attributes = $this->Skus->Attributes->find('list');
$images = $this->Skus->Products->getMedias('list', $product_id, 'photo');
$this->set(compact('skus', 'products', 'attributes', 'images', 'product_id'));
$this->set('_serialize', ['skus']);
}
Controller posted data:
[
'product_id' => '65',
'attributes' => [
'_ids' => ''
],
'reference' => '',
'quantity' => '420',
'is_default' => '0',
'images' => [
'_ids' => [
(int) 0 => '90'
]
]
]
Forgot to add the name of the association in the patchEntity associated option. Still shouldn't throw a fatal error so I created a github ticket.

Categories