Laravel:模型ID在更新前变为true

时间:2018-10-31 11:49:00

标签: php laravel eloquent

我有一个很奇怪的情况。我有一段代码,过去从未出现任何问题。从昨晚开始,它的行为就这样。

在更新模型之前,该模型的ID为true。下面的函数来自控制器,并通过POST请求进行调用。该请求得到验证,并且当尚未导出模型时,它将导出到另一个系统。如果导出成功,则将使用适当的值更新模型。在此过程的任何阶段都不会设置ID。

我已经在代码中添加了注释,以使您了解发生的情况。

    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()解析为该功能。在此之前,该ID仍然是模型的原始ID。

    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());在此行之后,将id设置为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,
        ];
    }

目前,我已经添加了一个临时修复程序来缓解此问题。我创建了$发票的副本。以后,我将原始发票的ID设置为克隆的发票ID。

...
    $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的位置。我仍然不知道为什么。

在生成PDF之前的VeniceInvoiceResource $this->id中,该ID仍然是原始发票ID。在打印机之后,标识为true

如果我查看在Illuminat\Http\Resources\JsonResource(资源扩展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语句足以应付这种情况。

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

如果仔细观察,您会发现第一个条件不是条件...存在问题,并且修复很简单。删除此if语句,因为我们不再需要它。发票4128 & 4217已被打印并存档。它们不再需要处理。

1 个答案:

答案 0 :(得分:2)

好像您在printData()方法中发现了问题。

为什么id最终以true结束,这是由于operator precedences的不同所致。

比较运算符(===)的优先级高于逻辑运算符(||),因此在逻辑比较之前进行比较。因此,如果比较运算符正确,那么就可以运行该程序(为清楚起见添加了括号):

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

但是,因为第一个运算符实际上是赋值而不是比较,所以这改变了操作顺序。比较运算符和逻辑运算符的优先级高于赋值运算符,因此它们首先被执行。这是实际运行的内容(为清晰起见添加了括号):

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

因此,id被分配给逻辑比较的结果。由于所有非零数字的求值为true,因此逻辑比较的求值为true,因此id被设置为true