Laravel: анализировать объект JSON с массивом «подобъектов» для экземпляра модели

В моем приложении (Laravel) вы получите JSON, который выглядит так:

{
  "name": "order 1",
  "customer": "cus123",
  "orderItems": [
    {
      "amount": 1,
      "name": "cola",
      "price": "2.10"
    },
    {
      "amount": 3,
      "name": "fanta",
      "price": "2.00"
    },
  ]
}

Я создал 2 модели в Laravel, одну Order и одну OrderItem. Я хочу разобрать полученный JSON на один Order экземпляр $order. Я могу сделать это, выполнив это в моем OrderController:

class OrderController extends Controller
{
  public function store(Request $request) {
    $order = new Order();
    $order->forceFill($request->toArray());
  }
}

Теперь можно получить доступ к свойствам, таким как $order->name и $order->customer, в функции store контроллера. Когда я обращаюсь к $order->orderItems, я получаю массив с «orderItemsbut as array, not as instance ofOrderItem`.

Я хочу, чтобы $order->orderItems возвращал массив экземпляров OrderItem. Я попробовал следующее в Order, но это не работает, поскольку 'orderItems' не является OrderItem::class, а представляет собой массив с несколькими «OrderItems».

  protected $casts = [
    'orderItems' => OrderItem::class,
  ];

Как мне добиться, чтобы $order->orderItems возвращал массив экземпляров OrderItem? Спасибо за любую помощь заранее!

не забывайте проверять все запросы, поступающие от вашего пользователя, никогда не доверяйте своему пользователю, включая себя, когда речь идет об изменении вашей базы данных ^_^

Win 03.02.2023 12:42

Я не уверен, чего вы пытаетесь достичь, но я вижу 2 проблемы. 1. Вы не можете преобразовать несуществующий атрибут в свою модель, 2. Приведение - это преобразование атрибута модели в общий тип данных, вы не можете передавать туда случайные вещи laravel.com/docs/9.x/eloquent-mutators #приведение атрибутов

silver 03.02.2023 13:00
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
2
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Попробуйте добавить следующее в свой контроллер

  • Проверка
  • ручное хранение вашего заказа
  • ручное хранение каждой позиции вашего заказа

.

class OrderController extends Controller
{
  public function store(Request $request) 
  {
    $your_rules = [
       'name' => 'required|string',
       'customer' => 'required|string', // related to customer id ?
       'orderItems' => 'array',
       'orderItems.*.name' => 'string',
       'orderItems.*.amount' => 'integer|gte:1',
       'orderItems.*.price' => 'numeric|between:0,99.99',
    ];

    $validated = $request->validate($your_rules);
    $order = Order::create([
       'name' => $validated['name'],
       'customer' => $validated['customer'], // is this customer id or name ?
    ]);

    // I assume you already declare relationship to OrderItem inside your Order model
    foreach ($validated['orderItems'] as $orderItem) {
        // this array only is optional
        $orderItem = Arr::only($orderItem, ['name', 'amount', 'price');

        $order->orderItems()->save($orderItem);
    }

    // reload saved order items
    $order->load('orderItems');

    dd($order);
  }
}

Вы также можете создать несколько дочерних элементов одной командой.

$order->orderItems()->saveMany([
    new OrderItem(['name' => '...', ... ]),
    new OrderItem(['name' => '...', ... ]),
]);

Подробнее читайте здесь https://laravel.com/docs/9.x/eloquent-relationships#the-save-method

Вы можете переместить это в свою модель как дополнительный пользовательский метод. Например:

public function saveOrderItems(array $orderItems): void
{
   $this->orderItems()->saveMany($orderItems);
}

И вы называете это как $order->saveOrderItems($orderItems);

P.S.

Не забудьте объявить отношения в модели Order.

public function orderItems()
{
    return $this->hasMany(OrderItem::class);
}

Я знаю о проверке и т. д., Но это не имело отношения к вопросу, все равно спасибо, я реализую это так! Я тоже пользуюсь $fillable = ;-). Я мог бы сам прийти к твоему решению, глупый. Но интересно, нет ли способа сделать это с помощью одной команды? Я имею в виду, что синтаксический анализ JSON для объектов - это такая обычная вещь, что я не могу себе представить, что нет более простого способа сделать это.

CodeNinja 03.02.2023 15:01

вы можете добавить всю свою логику в класс обслуживания и вызвать ее из своего контроллера, это будет одна команда, правильно ^_^. ... в любом случае, если вы делаете только создание порядка, это будет одна команда создания, проблема в отношениях ... для одной дополнительной команды, которую вы можете использовать ->saveMany .. я обновлю свой ответ с помощью этого метода

Win 03.02.2023 16:02

я забыл добавить проверку для orderItems в виде массива и его содержимого, вы можете установить «количество», «имя» и «цену» по мере необходимости

Win 06.02.2023 12:47

Я думаю, вы запутались во всех отношениях модели. Ознакомьтесь с документацией здесь, вам нужно определить правильное отношение и внешний ключ между вашими моделями Order и OrderItem.

Тогда ваша модель должна быть такой;

//Order.php
class Order extends Model {

    protected $fillable = [
        'name',
        'customer',
    ];

    public function items() {
        return $this->hasMany(OrderItem::class);
    }
    
}

//OrderItem.php
class OrderItem extends Model {

    protected $fillable = [
        'amount',
        'name',
        'price'
    ];

    public function order() {
        return $this->belongsTo(Order::class);
    }
    
}

Тогда ваш метод магазина

public function store( Request $request ) {

    $request->validate([
        'name' => 'required',
        'customer' => 'required|exists:customers_table,id',
        'orderItems' => 'required|array'
    ]);

    $order = Order::create( $request->except('orderItems') );

    $items = $order->items()->createMany( $request->input('orderItems') );
}

Другие вопросы по теме