Laravel: идентификатор модели становится истинным перед обновлением - PullRequest
0 голосов
/ 31 октября 2018

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

Перед обновлением модели идентификатор этой модели становится истинным. Функция ниже из контроллера и вызывается с запросом 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()); После этой строки идентификатор устанавливается как true. Это действительно не имеет никакого смысла, поскольку это всегда работало, и модель никак не манипулируется.

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

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();
...

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

В VeniceInvoiceResource $this->id до генерации PDF идентификатор остается исходным идентификатором счета. После принтера идентификатор: true.

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

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

В то время как в VeniceInvoiceResource ресурс $ this-> передается экземпляру Printer. В ресурсе $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.

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

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

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

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

1 Ответ

0 голосов
/ 31 октября 2018

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...