Skip to main content

การทำให้ Laravel รองรับ Primary Key แบบหลาย Columns (Composite Keys)

ปัจจุบัน Eloquent ของ Laravel Framework ยังไม่รองรับการบันทึกข้อมูลที่มี Primary Key แบบหลาย Columns ได้ ผมได้ค้นหาจากอินเตอร์เน็ต และพบว่ามีคนที่แนะนำแนวทางแก้ไข โดยใช้วิธีการ Overide method save ของ Eloquent ขึ้นมาแทน ซึ่งผมได้ทดลองใช้แล้วสามารถทำงานได้อย่างถูกต้อง จึงขอแนะนำการนำ Method save ที่ถูกปรับปรุงมาเขียนเป็น Base Class ของ Model ดังตัวอย่าง


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use DB;

class BaseModel extends Model
{

    public function save(array $options = []) {
        if( ! is_array($this->getKeyName()))
        {
            return parent::save($options);
        }

        // Fire Event for others to hook
        if($this->fireModelEvent('saving') === false) return false;

        // Prepare query for inserting or updating
        $query = $this->newQueryWithoutScopes();

        // Perform Update
        if ($this->exists)
        {
            if (count($this->getDirty()) > 0)
            {
                // Fire Event for others to hook
                if ($this->fireModelEvent('updating') === false)
                {
                    return false;
                }

                // Touch the timestamps
                if ($this->timestamps)
                {
                    $this->updateTimestamps();
                }

                //
                // START FIX
                //


                // Convert primary key into an array if it's a single value
                $primary = (count($this->getKeyName()) > 1) ? $this->getKeyName() : [$this->getKeyName()];

                // Fetch the primary key(s) values before any changes
                $unique = array_intersect_key($this->original, array_flip($primary));

                // Fetch the primary key(s) values after any changes
                $unique = !empty($unique) ? $unique : array_intersect_key($this->getAttributes(), array_flip($primary));

                // Fetch the element of the array if the array contains only a single element
                //$unique = (count($unique) <> 1) ? $unique : reset($unique);

                // Apply SQL logic
                $query->where($unique);

                //
                // END FIX
                //

                // Update the records
                $query->update($this->getDirty());

                // Fire an event for hooking into
                $this->fireModelEvent('updated', false);
            }
        }
        // Insert
        else
        {
            // Fire an event for hooking into
            if ($this->fireModelEvent('creating') === false) return false;

            // Touch the timestamps
            if($this->timestamps)
            {
                $this->updateTimestamps();
            }

            // Retrieve the attributes
            $attributes = $this->attributes;

            if ($this->incrementing && !is_array($this->getKeyName()))
            {
                $this->insertAndSetId($query, $attributes);
            }
            else
            {
                $query->insert($attributes);
            }

            // Set exists to true in case someone tries to update it during an event
            $this->exists = true;

            // Fire an event for hooking into
            $this->fireModelEvent('created', false);
        }

        // Fires an event
        $this->fireModelEvent('saved', false);

        // Sync
        $this->original = $this->attributes;

        // Touches all relations
        if (array_get($options, 'touch', true)) $this->touchOwners();

        return true;
    }    
}

โดยการนำเอา BaseModel นี้ไปใช้ในรูปแบบการ Extend Model Class ดังต่อไปนี้


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\BaseModel;

class MyTable extends BaseModel
{

    protected $table = 'MyTable';
    protected $primaryKey = ['MY_KEY_COLUMN_1', 'MY_KEY_COLUMN_2', 'MY_KEY_COLUMN_3'];
    
    public $incrementing = false;
    public $timestamps = false;

}


และสามารถเรียกใช้งานได้เป็นปกติได้เลย เช่น


1
2
3
4
5
6
7
$myTable = new MyTable;
$myTable->FIELD1 = 'Feild1_Value';
$myTable->FIELD2 = 'Field2_Value';
$myTable->MY_KEY_COLUMN1 = 'Key1_Value';
$myTable->MY_KEY_COLUMN2 = 'Key2_Value';
$myTable->MY_KEY_COLUMN3 = 'Key3_Value';
$myTable->save();

Credits

- ขอบคุณข้อมูล Method Save จากคคุณ tsilenzio ซึ่งได้ post ไว้ใน https://github.com/laravel/framework/issues/5517


Comments

Popular posts from this blog

Facebook's Platform Update on Publish Permission

Over the past few months, we've continued to make significant changes to Facebook to better protect your information. This has meant working closely with developers to make sure they can adapt their apps to our new, tighter rules, while also continuing to offer people useful social experiences, like playing games or sharing playlists with friends. In a blog post on April 24, we announced that the publish_actions permission — which grants apps access to publish posts to Facebook as the logged in user — would be deprecated by August 1. Roughly 60,000 apps will lose access to the publish_actions permission on August 1, and we encourage developers to switch to Facebook's Share dialogs for web, iOS and Android. However, this timing does not give some desktop apps and hardware partners enough time to make the switch as they have very long product lifecycles. Therefore, we've granted 6-month and 12-month extensions respectively to these categories of developers. These developers...

Serialize and Deserialize JSON data with C#

This post is about example for serialize and deserialize json data with C#. This example using Newtonsoft.Json to be a library to work with json data. You can add Newtonsoft.Json by download from  https://www.newtonsoft.com/json  or using Nuget to add it into your project. In this example is Bill object that hold billing data about Car object. Serialize Object to String Below is example code to serialize object to json string. At line 39 is code to convert object into json string with beautiful json format. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace JSON { class JSonExample { static void Main( string [] args) { List<Car> cars = new List<Car>(); float to...

jQuery 3.3.1 – fixed dependencies in release tag

We encountered an issue in the release for jQuery 3.3.0, so we’ve immediately released another tag. The code itself is identical, but our release dependencies (only used during release) were added to the dependencies of the jQuery package itself due to the new behavior of npm in version 5+. jQuery 3.3.1 is now recommended if installing from npm or GitHub. If using jQuery on any CDN, the built file only differs in the version number. We apologize for any inconvenience and have updated our release script to account for this issue. Please see the jQuery 3.3.0 blog post for all relevant code changes in this release. Download You can get the files from the jQuery CDN, or link to them directly: https://code.jquery.com/jquery-3.3.1.js https://code.jquery.com/jquery-3.3.1.min.js You can also get this release from npm: npm install jquery@3.3.1 Slim build Sometimes you don’t need ajax, or you prefer to use one of the many standalone libraries that focus on ajax requests. A...