如何在Laravel中使用雄辩的方式获取最后插入的非增量ID?

时间:2018-12-30 17:27:05

标签: php laravel laravel-5

我有两个模型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');
    }
}

我的地址模型如下所示:

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

1 个答案:

答案 0 :(得分: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->customer_id不应为null。

编辑:无法注意到以下行:

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)。我怀疑在这种情况下,它无法获取适当的密钥,这就是为什么它失败了。我认为在这种情况下,您将不得不从customer_id表中获取sequence_customers

也许您可以通过执行以下操作来摆脱困境:

// 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;

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