Laravel: идентификатор модели становится истинным перед обновлением

У меня довольно странная ситуация. У меня есть фрагмент кода, который раньше никогда не вызывал проблем. С прошлой ночи он ведет себя вот так.

Перед обновлением модели идентификатор этой модели становится истинным. Приведенная ниже функция поступает от контроллера и вызывается с помощью запроса POST. Запрос проверяется, и когда модель не была экспортирована, она экспортируется в другую систему. Если экспорт успешен, модель обновляется с соответствующими значениями. Идентификатор не устанавливается ни на одном этапе этого процесса.

Я добавил комментарии к коду, чтобы вы знали, где и что происходит.

    public function export(Request $request, VeniceService $service, Invoice $invoice)
    {
        $invoice = $invoice->load([
            'user', 'customer', 'extension.referenceValues.definition', 'lines'
        ]);
        $this->enforce([
            new CheckCstNum($invoice->customer),
            new CheckReferences($invoice->extension),
        ], 432);

        if ($invoice->to_export) {
            DB::beginTransaction();
            try {
                var_dump($invoice->id); // returns the id
                $data = $service->export($invoice);
                var_dump($invoice->id); // returns the true
                $invoice->book_date = Carbon::now();
                $invoice->doc_num = $data['doc_num'];
                $invoice->sys_num = $data['sys_num'];
                $invoice->tsm_num = $data['tsm_num'];

                $invoice->to_export = false;
                $invoice->is_ticked = false;


                var_dump($invoice->id); // This returns true
                var_dump($invoice); // All the values are correct, except the id, this is set to true

                $invoice->save(); // With the id as true, this throws an exception. Duplicate entries for PRIMARY key id, '1'

                DB::commit();
                $service->attachPdf($invoice, Printer::print($invoice)->output());
            } catch (VeniceException $e) {
                DB::rollBack();
                return $e->render($request);
            } catch (\Exception $e) {
                DB::rollBack();
                return response()->json($e->getMessage(), 500);
            }
        }

        return new InvoiceResource($invoice->refresh()); // returns the invoice, but the id is still true
    }

$this->service->export() разрешает эту функцию. До того, как это произойдет, идентификатор по-прежнему является исходным идентификатором модели.

    public function export($model)
    {
        return $this->call($model, __FUNCTION__);
    }

    protected function call($model, $function)
    {
        $class = $this->getClassName($model);
        $method = "{$function}{$class}";
        return $this->$method($model);
    }

    public function exportInvoice($invoice)
    {
        var_dump($invoice->id); // Returns the id
        $veniceInvoice = (new VeniceInvoiceResource($invoice))->toArray(request());
        var_dump($invoice->id); // Returns true...

        return $this->request('POST', 'venice/invoices/' . $this->bookSales, [
            RequestOptions::JSON => $veniceInvoice
        ]);
    }

$veniceInvoice = (new VeniceInvoice($invoice))->toArray(request()); После этой строки идентификатор устанавливается как истина. Это действительно не имеет никакого смысла, поскольку это всегда работало, и модель не подвергается никаким манипуляциям.

Последний фрагмент кода. Но я не думаю, что это имеет какое-то отношение к проблеме.

VeniceInvoiceResource.php

    public function toArray($request)
    {
        $pdf = Printer::print($this->resource)->output();

        $lines = $this->customer->standard_base == 10 ? VeniceInvoiceLineIC::collection($this->lines) : VeniceInvoiceLine::collection($this->lines);
        $refs = $this->extension->referenceValues->map(function ($item) {
            return [
                'index' => 0,
                'quantity' => 0,
                'unit_price' => 0,
                'description' => $item->definition->name . ' ' . $item->value,
                'vat_code' => 0,
                'ic_code' => 0,
            ];
        })->toArray();

        $details = array_merge($refs, $lines->toArray($request));

        return [
            'cst_num' => $this->customer->cst_num,
            'book' => ($this->book === 'VKPCN') ? $this->book : config('venice.config.sales.book'),
            'amount' => $this->total,
            'vat_amount' => $this->total,
            'exp_date' => carbon(config('venice.config.sales.date'))->addDays($this->customer->exp_term)->format('d/m/Y'),
            'doc_date' => carbon(config('venice.config.sales.date'))->format('d/m/Y'),
            'vat_system' => $this->customer->vat_system,
            'bf_code' => $this->customer->bf_code,
            'doc_type' => ($this->doc_type === 'slsCreditnote') ? 1 : 0, 
            'pdf' => base64_encode($pdf),
            'pdfName' => $this->date->format('Ym') . '-' . $this->id . '.pdf',
            'remark' => 'Clockwork ' . $this->date->format('Y F') . ' ' . $this->user->name,
            'details' => $details,
        ];
    }

На данный момент я добавил временное исправление, чтобы смягчить проблему. Я создал копию счета-фактуры $. позже я установил идентификатор исходной накладной на идентификатор клонированной накладной.

...
    $invoice_copy = clone $invoice;

    if ($invoice->to_export) {
        DB::beginTransaction();
        try {
            $data = $service->export($invoice);

            $invoice->book_date = Carbon::now();
            $invoice->doc_num = $data['doc_num'];
            $invoice->sys_num = $data['sys_num'];
            $invoice->tsm_num = $data['tsm_num'];

            $invoice->to_export = false;
            $invoice->is_ticked = false;
            $invoice->id = $invoice_copy->id;
            $invoice->save();
            DB::commit();
...

После долгой отладки я определил, где для идентификатора установлено значение true. Я до сих пор не знаю почему.

В VeniceInvoiceResource $this->id до создания PDF-файла идентификатор по-прежнему является идентификатором исходного счета-фактуры. После принтера идёт true.

Если я посмотрю на конструктор ресурсов, найденных в Illuminat\Http\Resources\JsonResource (Resource extends JsonResource), я вижу, что $this->resource установлен на входящее значение, в данном случае $invoice.

    /**
     * Create a new resource instance.
     *
     * @param  mixed  $resource
     * @return void
     */
    public function __construct($resource)
    {
        $this->resource = $resource;
    }

В VeniceInvoiceResource ресурс $ this-> передается экземпляру принтера. В ресурсе $this также есть значения накладной.

    /**
     * Load items to print.
     *
     * @param $items
     * @return $this
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     */
    public function print($items, $toPrint = null)
    {
        $items = is_array($items) || $items instanceof Collection ? $items : func_get_args();

        foreach ($items as $item) {
            if ($item instanceof Printable) {
                foreach ($item->printData($toPrint) as $key => $data) {
                    switch($key) {
                        case 'merge':
                            $this->mergeOutput($data);
                            break;
                        case 'mergeFile':
                            $this->mergeFile($data);
                            break;
                        default:
                            $this->toPrint[] = $this->view->make($key, $data)->render();
                    }
                }
            } elseif ($item instanceof Renderable) {
                $this->toPrint[] = $item->render();
            } elseif (is_string($item)) {
                $this->toPrint[] = $item;
            } else {
                throw new \InvalidArgumentException('Invalid argument');
            }
        }

        return $this;
    }

В методе печати в этом случае используется $this->toPrint[] = $this->view->make($key, $data)->render();. Метод вывода выглядит так.

    /**
     * Get the output as string.
     *
     * @return string
     * @throws \iio\libmergepdf\Exception
     */
    public function output()
    {
        return $this->generate();
    }

    /**
     * Generate and merge.
     *
     * @return string
     * @throws \iio\libmergepdf\Exception
     */
    protected function generate()
    {
        $data = !empty($this->toPrint) ? $this->toPrint : [''];
        $result = $this->pdf->getOutputFromHtml($data); 

        if (!empty($this->toMerge)) {
            $this->merger->addRaw($result);

            foreach ($this->toMerge as $item) {
                $this->merger->addRaw($item);
            }
            $result = $this->merger->merge();
        }

        $this->toPrint = null;
        $this->toMerge = null;

        return $result;
    } 

В службе печати ничем не манипулируют, она просто печатает коллекции и элементы в формате PDF.

Последнее редактирование, потому что я нашел строку, которая все это вызвала. Но я не совсем понимаю, почему он устанавливает идентификатор в значение true.

В Printer::print есть вызов метода модели, printData() этот метод имеет оператор if для решения проблемы, которая возникла у нас с двумя счетами-фактурами, которые требовали особой обработки. Времени было не так много, поэтому мы решили, что для этой ситуации достаточно простого оператора if.

        if ($this->id = 4128 || $this->id === 4217) {
            $vat_amount = 0;
            $vat_label = '';
        }

если вы присмотритесь, то увидите, что первое условие не является условием ... Проблема есть, и исправить ее было просто. Удалите этот оператор if, поскольку он нам больше не нужен. Счета 4128 & 4217 уже распечатаны и заархивированы. Их больше не нужно обрабатывать.

Каково его значение до и после array_merge?

Matt Jameson 31.10.2018 12:59

@MattJameson Я немного расскажу о значениях. Я не в офисе после полудня

Odyssee 31.10.2018 13:07

Это не кажется разумным, но ставьте $data = $service->export($invoice); перед \DB::beginTransaction(), так как он не имеет ничего общего с БД.

ako 31.10.2018 13:11

Немного сложно читать, но вы делаете запрос в другую систему, и это то, что возвращается вам, если я прав? если так, то проблема в другом

abr 31.10.2018 13:12

@abr Я так не думаю, поскольку модель $invoice не была передана здесь функции export ($data = $service->export($invoice); по ссылке, поэтому ее свойства не должны изменяться вне первого вызова в контроллере.

ako 31.10.2018 13:14

Покажи мне свой ресурс для выставления счета @ako

abr 31.10.2018 13:25

@ako $invoice - это объект. Все объекты передаются по ссылке. Итак, если $invoice->id изменить где-нибудь в стеке вызовов, изменение будет видно в вызывающей функции.

patricus 31.10.2018 13:42

@IlyasDeckers Как выглядит конструктор VeniceInvoiceResource?

patricus 31.10.2018 13:43

@abr Это не проблема с внешней службой. Внешняя служба возвращает массив со значениями, эти значения устанавливаются в счете-фактуре. На данный момент идентификатор счета-фактуры уже верен. Чего я до сих пор не понимаю, так это того, что это никогда не дало никаких проблем. С другой стороны, я взял на себя этот проект, и большая часть кода не моя. Может я что то сломал на последнем обновлении. Я сейчас возвращаюсь к предыдущему выпуску локально, чтобы проверить, сохраняется ли проблема.

Odyssee 31.10.2018 14:10

@patricus У ресурса нет конструктора.

Odyssee 31.10.2018 14:10

@MattJameson Значения до и после array_merge точно такие же. Идентификатор правильный. Я сузил проблему до функции exportInvoice() Перед вызовом ресурса идентификатор не изменился. После того, как идентификатор верен. Я дополню свой вопрос этой информацией.

Odyssee 31.10.2018 14:22

Я бы подумал, что у VeniceInvoiceResource есть конструктор, поскольку он принимает параметры

Matt Jameson 31.10.2018 14:29

@MattJameson VeniceInvoiceResource расширяет ресурс. конструктор используется из Ресурса, написанного разработчиками Laravel. Я совершенно уверен, что проблема в VeniceInvoiceResource. Объект $this имеет значение true как id после вызова Printer :: print (). Этот принтер не меняет никаких значений. Он создает PDF-файл только из заданных значений. Я буду продолжать тестирование и обновлять вопрос, если найду соответствующую информацию.

Odyssee 31.10.2018 14:47

@IlyasDeckers Вы проверяли $this->resource->id до и после звонка на Printer::print($this->resource)->output()? Если его поменяли, то как выглядит метод print() принтера?

patricus 31.10.2018 15:37

@patricus Я обновил свой вопрос функцией печати и некоторой другой информацией, которая может быть актуальной. $this->resource->id ведет себя так же, как $this->id. После метода print () идентификатор равен true.

Odyssee 31.10.2018 15:51

Наконец-то нашел ... Для ответа посмотрите последнюю часть вопроса.

Odyssee 31.10.2018 16:49

@IlyasDeckers Рад, что вы его нашли. Мой следующий вопрос должен был заключаться в том, что было в методе printData().

patricus 31.10.2018 17:47
0
17
108
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Похоже, вы нашли проблему в своем методе printData().

Почему id заканчивается как true, это связано с другим приоритеты операторов.

Операторы сравнения (===) имеют более высокий приоритет, чем логический оператор (||), поэтому сравнения выполняются перед логическим сравнением. Итак, если бы оператор сравнения был правильным, это то, что было бы выполнено (для ясности добавлены скобки):

($this->id === 4128) || ($this->id === 4217)

Однако, поскольку первый оператор был фактически присваиванием, а не сравнением, это изменило порядок работы. Операторы сравнения и логические имеют более высокий приоритет, чем оператор присваивания, поэтому они выполняются первыми. Вот что было выполнено на самом деле (для ясности добавлены паренсы):

$this->id = (4128 || $this->id === 4217)

Итак, id попал в результат логического сравнения. Поскольку все ненулевые числа оцениваются как true, логическое сравнение оценивается как true, и, следовательно, id был установлен как true.

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