【问题标题】:How to get last inserted non incremental id using eloquent in Laravel?如何在 Laravel 中使用 eloquent 获取最后插入的非增量 id?
【发布时间】:2019-05-27 12:53:27
【问题描述】:

我有两个型号CustomerAddress。我的Customer 具有非增量主键,类型为string,即customer_id。这两个模型之间是一对多的关系,即对于单个customer 多个addresses 例如:发票地址、收货地址、当前地址等。我的Customer 模型如下图:

Customer.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    protected $keyType = 'string';
    protected $primaryKey = 'customer_id';
    public $incrementing = false;

    public function addresses()
    {
        return $this->hasMany('App\Address','customer_id');
    }
}

而我的地址模型如下图:

地址.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Address extends Model
{
    //
    public $timestamps = false;
    // protected $table = "addresses";

    public function customer()
    {
        return $this->belongsTo('App\Customer');
    }
}

下面显示了我的客户表的迁移

客户迁移表

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCustomersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->string('customer_id');
            $table->string('name');
            $table->string('type');
            $table->date('dob');
            $table->type('country_code');

            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('customers');
    }
}

要注意的另一件事是,我的customer_id 是增量的,因为我创建了单独的表,即customer_sequence,它是自动增量的,在插入记录之前,我使用触发器附加两个字符代码,然后将其放入我的customers 表。 我的customer_sequence迁移如下图

customer_sequence 迁移

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateSequenceCustomers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('sequence_customers', function (Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('sequence_customers');
    }
}

我用于插入递增字符串 id 的触发器如下:

customer_id 触发器的迁移

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTriggerCustomers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::unprepared("
        CREATE TRIGGER tg_customer_insert
            BEFORE INSERT ON customers
            FOR EACH ROW
            BEGIN
                INSERT INTO sequence_customers(id) VALUES (NULL);
                IF NEW.type ='Private' THEN
                    SET NEW.customer_id = CONCAT(NEW.country_code, LPAD(LAST_INSERT_ID(), 5, '0'));
                ELSEIF NEW.type='Business' THEN
                    SET NEW.customer_id = CONCAT(NEW.country_code, LPAD(LAST_INSERT_ID(), 5, '0'));
                ELSEIF NEW.type='Reseller' THEN
                    SET NEW.customer_id = LPAD(LAST_INSERT_ID(), 5, '0');
                ELSEIF NEW.type='Distributor' THEN
                    SET NEW.customer_id = LPAD(LAST_INSERT_ID(), 5, '0');
                ELSEIF NEW.type='Other' THEN
                    SET NEW.customer_id = LPAD(LAST_INSERT_ID(), 5, '0');
                END IF;
                IF NEW.credit_amount > NEW.credit_limit THEN
                   SET NEW.credit_limit_exceeded=TRUE;
                ELSE
                    SET NEW.credit_limit_exceeded=FALSE;
                END IF;
            END
        ");
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        DB::unprepared('DROP TRIGGER IF EXISTS tg_customer_insert');
    }
}

现在,当我保存客户的数据并尝试从客户模型中获取 id 时,它返回给我 null。我的控制器如下图:

CustomerController.php

public function store(Request $request)
{
    $customer = new Customer;
    $invoiceAddress = new Address;
    $deliveryAddress = new Address;

    $customer->name = $request->name;
    $customer->type = $request->type;
    $customer->dob = $request->dob;
    $customer->country_code=$request->country_code;
    $customer->save();

    $deliveryAddress->street_name_no = $request->street_name_no;
    $deliveryAddress->city = $request->city;
    $deliveryAddress->country = $request->country;

    //This throws error customer_id cannot be null integrity constraint
    $deliveryAddress->customer_id = $customer->customer_id;
    $deliveryAddress->save();
}

【问题讨论】:

    标签: php laravel laravel-5


    【解决方案1】:

    这是因为您将请求值分配给客户变量。

    $customer=new Customer;
    
    $customer=$request->name;
    $customer=$request->type;
    $customer=$request->dob;
    $customer->save();
    

    当你调用save() 时,你实际上是在一个字符串上调用save()。 通过在 Customer 模型上指定可填充属性来修复它。这只是一个例子。

    $customer = new Customer();
    
    $customer->name = $request->name;
    $customer->type = $request->type;
    $customer->dob  = $request->dob;
    $customer->save();
    

    之后,$customer-&gt;customer_id 不应为空。

    编辑:未能注意到以下行:

    public $incrementing = false;
    

    这意味着在创建Customer 时,您还必须提供customer_id,因为它不再是自动递增的。

    我还深入了解了 API。似乎 Laravel 在那个阶段不会知道触发器设置的属性。您可以尝试 refresh() 模型,该模型将从数据库中提取新属性,并假设您的触发器工作正常,您应该得到 customer_id

    所以本质上,只需在添加收货地址之前添加这一行。

    $customer->refresh();
    

    我还注意到您没有任何逻辑将用户重定向回成功保存。 我怀疑这就是它抛出 404 的原因,因为没有为 GET 请求定义相同的路由。

    public function store(Request $request)
    {
        $customer        = new Customer;
        $invoiceAddress  = new Address;
        $deliveryAddress = new Address;
    
        $customer->name = $request->name;
        $customer->type = $request->type;
        $customer->dob  = $request->dob;
        $customer->country_code = $request->country_code;
    
        $customer->save();
    
        $customer->refresh(); 
    
        $deliveryAddress->street_name_no = $request->street_name_no;
        $deliveryAddress->city = $request->city;
        $deliveryAddress->country = $request->country;
    
    
        $deliveryAddress->customer_id = $customer->customer_id;
        $deliveryAddress->save();
    
        return back()->with('success', 'Success message here');
    }
    

    再次编辑:

    从文档看来,refresh()方法如下:

    /**
     * Reload the current model instance with fresh attributes from the database.
     *
     * @return $this
     */
    public function refresh()
    {
        if (! $this->exists) {
            return $this;
        }
    
        $this->setRawAttributes(
            static::newQueryWithoutScopes()->findOrFail($this->getKey())->attributes
        );
    
        $this->load(collect($this->relations)->except('pivot')->keys()->toArray());
    
        $this->syncOriginal();
    
        return $this;
    }
    

    从下面一行可以看出:

    static::newQueryWithoutScopes()->findOrFail($this->getKey())->attributes
    

    它会在刷新模型时尝试查找或失败 (404)。我怀疑在这种情况下,它无法获得适当的密钥,这就是它失败的原因。我认为在这种特殊情况下,您必须从sequence_customers 表中获取customer_id

    也许您可以通过以下方式摆脱困境:

    // Assuming SequenceCustomer is the model name
    $latest = \App\SequenceCustomer::latest()->first(); 
    
    // and then you would be able to access the latest customer_id by doing the following
    
    $customer_id = $latest->customer_id;
    

    这显然不是一个可扩展的解决方案,但我不太确定如何解决这个特定问题:)

    【讨论】:

    • 抱歉打错了,我已经更新了,请看一下。
    • 编辑了我的答案。
    • 我在我的问题中提到我正在使用名为“客户序列”的其他表中的序列,其中有我在触发器中使用的自动递增 id,然后为 ex SA001 附加代码字符在我的客户表中用作主键。
    • 再次编辑了我的答案。
    • 这并不能解决我的问题,现在我收到错误提示找不到页面。
    猜你喜欢
    • 2014-01-31
    • 2015-03-08
    • 2016-08-15
    • 2018-04-06
    • 2016-10-24
    • 2016-06-01
    • 2021-10-22
    相关资源
    最近更新 更多