【问题标题】:How I can put composite keys in models in Laravel 5?如何在 Laravel 5 的模型中放置复合键?
【发布时间】:2015-10-03 14:19:54
【问题描述】:

我的数据库中有一个包含两个主键(id 和 language_id)的表,我需要将它放入我的模型中。模型中的默认主键(Laravel 5 中的 Model.php)是 id,我希望主键是 id 和 id_language。我尝试将它与数组或带有“,”的字符串放在一起,但它不起作用。它说我无法将数组转换为字符串。

【问题讨论】:

标签: php laravel model composite-primary-key


【解决方案1】:

你不能。 Eloquent 不支持复合主键。

Here's a Github issue regarding this.

【讨论】:

  • 我一次又一次地回到这个答案,希望 Eloquent 能够获得复合键支持。该问题已被锁定。谁是谈论雄辩的合适人选?通过 src 有点过分了,我很想抢先一步,了解为什么很难获得复合支持。
  • 不知道是什么时候添加的,但它们在 Laravel 5.6+ 中可用 (laravel.com/docs/5.6/migrations#creating-indexes)
  • @HonestObjections 它们在迁移中可用,并且已用于多个版本。那不是问题。 Eloquent 仍然不支持复合键(出于某种原因)。
  • 我的错误,我认为如果他们在迁移中会有雄辩的支持
  • 现在支持
【解决方案2】:

它似乎改变了,因为这个至少适用于 Laravel 5.1:

$table->primary(['key1', 'key2']);

我只是运行迁移,我在数据库中看到的内容与我在上面的代码中输入的内容相符(当然上面的名称字段仅用于演示目的)。

更新:这适用于迁移,但只要您想通过 eloquent 插入,它就不能使用复合键并且永远不会这样做(最后一个条目):

https://github.com/laravel/framework/issues/5517

【讨论】:

    【解决方案3】:

    尝试查看此文档以插入与 CK 的多对多关系

    https://laravel.com/docs/5.2/eloquent-relationships#inserting-many-to-many-relationships

    编辑:一些额外信息
    正如您在文档中看到的,附加和分离函数创建了 CK 中间表中所需的链接。所以你不必自己创建它们;)

    在你的情况下是model->languages()->attach(language_id)

    【讨论】:

      【解决方案4】:

      我写了这个简单的PHP trait 来调整 Eloquent 来处理复合键:

      <?php
      
      namespace App\Model\Traits; // *** Adjust this to match your model namespace! ***
      
      use Illuminate\Database\Eloquent\Builder;
      
      trait HasCompositePrimaryKey
      {
          /**
           * Get the value indicating whether the IDs are incrementing.
           *
           * @return bool
           */
          public function getIncrementing()
          {
              return false;
          }
      
          /**
           * Set the keys for a save update query.
           *
           * @param  \Illuminate\Database\Eloquent\Builder $query
           * @return \Illuminate\Database\Eloquent\Builder
           */
          protected function setKeysForSaveQuery(Builder $query)
          {
              foreach ($this->getKeyName() as $key) {
                  // UPDATE: Added isset() per devflow's comment.
                  if (isset($this->$key))
                      $query->where($key, '=', $this->$key);
                  else
                      throw new Exception(__METHOD__ . 'Missing part of the primary key: ' . $key);
              }
      
              return $query;
          }
      
          // UPDATE: From jessedp. See his edit, below.
          /**
           * Execute a query for a single record by ID.
           *
           * @param  array  $ids Array of keys, like [column => value].
           * @param  array  $columns
           * @return mixed|static
           */
          public static function find($ids, $columns = ['*'])
          {
              $me = new self;
              $query = $me->newQuery();
              foreach ($me->getKeyName() as $key) {
                  $query->where($key, '=', $ids[$key]);
              }
              return $query->first($columns);
          }
      }
      

      将它放在你的主模型目录下的Traits 目录中,然后你可以在任何复合键模型的顶部添加一个简单的单行:

      class MyModel extends Eloquent {
          use Traits\HasCompositePrimaryKey; // *** THIS!!! ***
      
          /**
           * The primary key of the table.
           * 
           * @var string
           */
          protected $primaryKey = array('key1', 'key2');
      
          ...
      


      由 jessedp 添加:
      这对我来说非常有效,直到我想使用 Model::find ... 所以以下是一些可以添加到上面的 hasCompositePrimaryKey 特征的代码(可能会更好):
      protected static function find($id, $columns = ['*'])
      {
          $me = new self;
          $query = $me->newQuery();
          $i=0;
      
          foreach ($me->getKeyName() as $key) {
              $query->where($key, '=', $id[$i]);
              $i++;
          }
      
          return $query->first($columns);
      }
      

      2016 年 11 月 17 日更新

      我现在将其作为名为@9​​87654322@ 的开源包的一部分进行维护。

      2020 年 6 月 10 日更新

      LaravelTreats 已经死了,但还是享受代码吧 :)

      多年来,一些深入的用例引起了我的注意,这些用例出现了问题。这应该适用于绝大多数用例,但要知道,如果你想变得花哨,你可能需要重新考虑你的方法。

      【讨论】:

      • 注意如果你使用boolean类型的主键,你需要修复setKeysForSaveQuery()函数的if语句。 if($this-&gt;$key)if(isset($this-&gt;$key))
      • @jesse 这个find() 方法来自哪里?它在Illuminate\Database\Eloquent\Model 上不存在,并且在Illuminate\Database\Query\Builder 中是公开的而不是静态的
      • 在 Laravel 5.3 上使用 find() 时失败,追踪到 Illuminate\Database\Eloquent\Model 类的方法 getQualifiedKeyName()
      • 对于那些想知道如何在此处使用 find 的人:如果您的主要配对是 ['A','B'],那么您可以通过 Model::find(['A' =&gt; 'first_part_of_your_key', 'B' =&gt; 'second_part_of_your_key']) 找到您的条目。此外,复制和粘贴脚本在自定义方法 find 中包含错误,第一个参数应为 $id 而不是 $ids
      • @mopo922 我很震惊为什么很多人抱怨这个问题以及为什么 Laravel 的 autors 没有添加这个功能。既遗憾又遗憾。您的解决方案有效!非常感谢您为此付出的时间!但是,如果我尝试与belongToMany 建立关系,则会引发此异常:array_key_exists():第一个参数应该是字符串或整数。发生这种情况是因为它期望“primaryKey”是一个值,但实际上是一个数组。 ¿ 你知道一些解决方法吗?
      【解决方案5】:

      https://laravel.com/docs/5.3/migrations#columns

      是的,你可以。

      我分享我的迁移代码:

          <?php
      
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Database\Migrations\Migration;
      
      class RegistroEmpresa extends Migration {
      
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('registro_empresa', function(Blueprint $table)
              {
                  $table->string('licencia',24);
                  $table->string('rut',12);
                  $table->string('nombre_empresa');
                  $table->string('direccion');
                  $table->string('comuna_Estado');
                  $table->string('ciudad');
                  $table->string('pais');
                  $table->string('fono');
                  $table->string('email');
                  $table->string('paginaweb');
                  $table->string('descripcion_tienda');
                  $table->string('monedauso');
                  $table->timestamps();
                  $table->primary(['licencia', 'rut']);
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::drop('registro_empresa');
          }
      
      }
      

      【讨论】:

      • 创建一个表。您如何编写查询该表的工作模型?
      • 问题是关于模型,而不是迁移。
      【解决方案6】:

      在迁移中您可以像@erick-suarez 和@sba 所说的那样简单地为表定义复合主键,在您的Schema::createSchema::table 块中写入$table-&gt;primary(['key1', 'key2']);

      在表示该表的 Eloquent 模型中,您不能直接将复合键与 Eloquent 方法一起使用,例如find($key)save($data) 但您仍然可以使用

      检索模型实例以进行查看
      $modelObject = ModelName->where(['key1' => $key1, 'key2' => $key2])->first();
      

      如果您想更新该表中的记录,您可以使用QueryBuilder 方法,如下所示:

      ModelName->where(['key1' => $key1, 'key2' => $key2])->update($data);
      

      其中$data 是您要更新模型的数据关联数组,如['attribute1' =&gt; 'value1', ..]


      注意:您仍然可以安全地使用 Eloquent 关系来检索此类模型,因为它们通常用作打破多对多关系结构的数据透视表。

      【讨论】:

        【解决方案7】:

        你可以尝试使用以下模块

        https://github.com/maksimru/composite-primary-keys

        只需将 HasCompositePrimaryKey 特征添加到您的模型并将数组值指定为主键

        【讨论】:

          【解决方案8】:

          一个简单的解决方案,对我很有效(Laravel 5.7):

          模型中定义其中一个主键:

          class MyTable extends Model
          {
                  protected $primaryKey = 'user_id';
                  protected $table = 'my_table';
                  protected $fillable = ['user_id', 'post_id', 'flag'];
          

          所以在Controller中传递两个主键,这里我使用了updateOrCreate方法的两个参数中的第一个,它接收两个数组:

          控制器

          public function post_foo(Request $request)
          {
          //PK1 is user_id, PK2 is post_id
          //flag - is the field that has its value changed
          
          $task = MyTable::updateOrCreate(
              ['user_id' => $request->header('UID'),'post_id' => $request->input('post_id')],
              ['user_id' => $request->header('UID'),'post_id' => $request->input('post_id'),'flag' => $request->input('flag')]
              );
              return $task;
          }
          

          解释解决方案:

          您没有id 列作为主键的问题通过Model 中的一个主键解决了,通过Controller 中的两个主键找到DB 中的正确行。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-05-25
            • 1970-01-01
            • 1970-01-01
            • 2015-06-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多