엔티티 클래스 사용하기

CodeIgniter는 엔티티 클래스를 일급 시민으로 지원하면서도 사용을 완전히 선택적으로 유지합니다. 엔티티 클래스는 리포지토리 패턴의 일부로 자주 사용되지만, 필요에 따라 모델과 직접 함께 사용할 수도 있습니다.

엔티티 사용법

핵심적으로, 엔티티 클래스는 단순히 단일 데이터베이스 행을 나타내는 클래스입니다. 데이터베이스 컬럼을 나타내는 클래스 속성을 가지고 있으며, 해당 행에 대한 비즈니스 로직을 구현하기 위한 추가 메서드를 제공합니다.

참고

이해를 돕기 위해, 여기서의 설명은 데이터베이스를 사용하는 경우를 기반으로 합니다. 그러나 엔티티는 데이터베이스에서 오지 않는 데이터에도 사용할 수 있습니다.

그러나 핵심 특징은, 엔티티 자신이 어떻게 영속화되어야 하는지에 대해 전혀 알지 못한다는 점입니다. 그것은 모델 또는 리포지토리 클래스의 책임입니다. 따라서 객체를 저장하는 방식에 변경이 생기더라도, 애플리케이션 전체에서 해당 객체가 사용되는 방식을 변경할 필요가 없습니다.

이를 통해 빠른 프로토타이핑 단계에서는 JSON 또는 XML 파일을 사용하여 객체를 저장하고, 개념이 작동함을 증명한 후에는 데이터베이스로 쉽게 전환할 수 있습니다.

이해를 돕기 위해 매우 간단한 User 엔티티와 그것을 어떻게 사용하는지 살펴보겠습니다.

다음과 같은 스키마를 가진 users라는 데이터베이스 테이블이 있다고 가정합니다:

id          - integer
username    - string
email       - string
password    - string
created_at  - datetime

엔티티 클래스 생성하기

이제 새 엔티티 클래스를 생성합니다. 이 클래스를 저장할 기본 위치가 없고 기존 디렉터리 구조에 맞지 않으므로, app/Entities에 새 디렉터리를 만드세요. 엔티티 자체는 app/Entities/User.php에 생성합니다.

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    // ...
}

가장 간단하게는, 이것이 필요한 전부입니다. 하지만 곧 더 유용하게 만들어 보겠습니다.

모델 생성하기

먼저 app/Models/UserModel.php에 모델을 생성하여 상호작용할 수 있도록 합니다:

<?php

namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $table         = 'users';
    protected $allowedFields = [
        'username', 'email', 'password',
    ];
    protected $returnType    = \App\Entities\User::class;
    protected $useTimestamps = true;
}

모델은 모든 작업에 데이터베이스의 users 테이블을 사용합니다.

$allowedFields 속성에 외부 클래스가 변경할 수 있도록 허용하려는 모든 필드를 설정했습니다. id, created_at, updated_at 필드는 클래스 또는 데이터베이스에서 자동으로 처리되므로 변경하지 않습니다.

마지막으로, 엔티티 클래스를 $returnType으로 설정했습니다. 이렇게 하면 데이터베이스에서 행을 반환하는 모델의 모든 내장 메서드가 일반적인 객체나 배열 대신 User 엔티티 클래스의 인스턴스를 반환합니다.

참고

물론, 모델에 커스텀 메서드를 추가하는 경우 $returnType의 인스턴스가 반환되도록 구현해야 합니다.

엔티티 클래스 사용하기

이제 모든 준비가 갖추어졌으므로, 다른 클래스와 동일하게 엔티티 클래스를 사용할 수 있습니다:

<?php

$user = $userModel->find($id);

// Display
echo $user->username;
echo $user->email;

// Updating
unset($user->username);

if (! isset($user->username)) {
    $user->username = 'something new';
}

$userModel->save($user);

// Create
$user           = new \App\Entities\User();
$user->username = 'foo';
$user->email    = 'foo@example.com';
$userModel->save($user);

User 클래스가 컬럼에 대한 속성을 설정하지 않았지만 마치 공개 속성인 것처럼 접근할 수 있다는 것을 눈치챘을 것입니다. 기본 클래스인 CodeIgniter\Entity\Entity가 이를 처리해 주며, isset()으로 속성을 확인하거나 속성을 unset()하고, 객체가 생성되거나 데이터베이스에서 가져온 이후 변경된 컬럼을 추적하는 기능도 제공합니다.

참고

엔티티 클래스는 내부적으로 클래스 속성 $attributes에 데이터를 저장합니다.

User를 모델의 save() 메서드에 전달하면, 속성을 읽고 모델의 $allowedFields 속성에 나열된 컬럼의 변경 사항을 자동으로 저장합니다. 또한 새 행을 생성할지, 기존 행을 업데이트할지도 자동으로 판단합니다.

참고

insert()를 호출할 때는 엔티티의 모든 값이 메서드에 전달되지만, update()를 호출할 때는 변경된 값만 전달됩니다.

속성 빠르게 채우기

엔티티 클래스는 fill() 메서드도 제공합니다. 이 메서드를 사용하면 키/값 쌍의 배열을 클래스에 넣어 클래스 속성을 채울 수 있습니다. 배열의 모든 속성이 엔티티에 설정됩니다. 하지만 모델을 통해 저장할 때는 $allowedFields에 있는 필드만 실제로 데이터베이스에 저장되므로, 불필요한 필드가 잘못 저장될 걱정 없이 엔티티에 추가 데이터를 저장할 수 있습니다.

<?php

$data = $this->request->getPost();

$user = new \App\Entities\User();
$user->fill($data);
$userModel->save($user);

생성자에 데이터를 전달할 수도 있으며, 인스턴스화 시 해당 데이터는 fill() 메서드를 통해 처리됩니다.

<?php

$data = $this->request->getPost();

$user = new \App\Entities\User($data);
$userModel->save($user);

속성 일괄 접근하기

엔티티 클래스는 사용 가능한 모든 속성을 배열로 추출하는 두 가지 메서드를 제공합니다: toArray()toRawArray()입니다. raw 버전을 사용하면 매직 “getter” 메서드와 캐스팅을 우회합니다. 두 메서드 모두 첫 번째 불리언 파라미터로 반환 값을 변경된 것들로 필터링할지 여부를 지정하고, 중첩된 엔티티의 경우 마지막 불리언 파라미터로 메서드를 재귀적으로 실행할지 지정할 수 있습니다.

숨겨진 속성

엔티티는 이름이 밑줄(_)로 시작하는 숨겨진 속성을 가질 수 있습니다. 기본적으로 이러한 숨겨진 속성은 toArray()를 호출할 때 포함되지 않습니다. 하지만 필요한 경우 직접 접근할 수 있습니다.

숨겨진 속성이 toArray() 출력에 나타나도록 하려면 datamap 기능을 사용해야 합니다 - 이 기능은 해당 속성을 “가시화”합니다.

스마트 __get()__set() 메서드(다음 섹션에서 설명)는 속성 이름의 선행 밑줄을 무시한다는 점에 유의하세요. 즉, 같은 이름을 가진 두 속성(하나는 _로 시작하고 하나는 그렇지 않은)이 모두 동일한 getter와 setter를 사용하므로, 기본으로 처리할 속성을 선택해야 합니다.

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $datamap = [
        '_role' => '_role',
    ];

    protected $attributes = [
        '__secure' => 'On',
        '_role'    => 'user',
        'about'    => '',
    ];
}

$user = new User(['__secure' => 'Off', 'about' => 'Hi, I am John!', '_role' => 'admin']);

echo 'Secure: ' . $user->__secure;
print_r($user->toArray());
print_r($user->toRawArray());

/**
 * Output:
 *
 * Secure: Off
 * Array
 * (
 *     [about] => Hi, I am John!
 *     [_role] => admin
 * )
 * Array
 * (
 *     [__secure] => Off
 *     [_role] => admin
 *     [about] => Hi, I am John!
 * )
 */

비즈니스 로직 처리하기

위의 예제들은 편리하지만, 비즈니스 로직을 강제하는 데는 도움이 되지 않습니다. 기본 엔티티 클래스는 특별한 메서드를 확인하고 속성을 직접 사용하는 대신 해당 메서드를 사용하는 스마트한 __get()__set() 메서드를 구현하여, 필요한 비즈니스 로직이나 데이터 변환을 강제할 수 있게 합니다.

이를 어떻게 사용할 수 있는지 몇 가지 예시를 보여주기 위해 업데이트된 User 엔티티를 살펴보겠습니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;
use CodeIgniter\I18n\Time;

class User extends Entity
{
    public function setPassword(string $pass)
    {
        $this->attributes['password'] = password_hash($pass, PASSWORD_BCRYPT);

        return $this;
    }

    public function setCreatedAt(string $dateString)
    {
        $this->attributes['created_at'] = new Time($dateString, 'UTC');

        return $this;
    }

    public function getCreatedAt(string $format = 'Y-m-d H:i:s')
    {
        // Convert to CodeIgniter\I18n\Time object
        $this->attributes['created_at'] = $this->mutateDate($this->attributes['created_at']);

        $timezone = $this->timezone ?? app_timezone();

        $this->attributes['created_at']->setTimezone($timezone);

        return $this->attributes['created_at']->format($format);
    }
}

먼저 주목할 점은 추가한 메서드의 이름입니다. 각각의 경우, 클래스는 snake_case 컬럼 이름이 PascalCase로 변환되고 set 또는 get이 접두사로 붙을 것을 기대합니다. 이 메서드들은 직접 구문(예: $user->email)을 사용하여 클래스 속성을 설정하거나 가져올 때마다 자동으로 호출됩니다. 다른 클래스에서 접근하려는 경우가 아니라면 메서드가 public일 필요는 없습니다. 예를 들어, created_at 클래스 속성은 setCreatedAt()getCreatedAt() 메서드를 통해 접근됩니다.

참고

이것은 클래스 외부에서 속성에 접근하려 할 때만 작동합니다. 클래스 내부의 메서드는 setX()getX() 메서드를 직접 호출해야 합니다.

setPassword() 메서드에서는 비밀번호가 항상 해시화되도록 보장합니다.

setCreatedAt()에서는 모델로부터 받은 문자열을 DateTime 객체로 변환하여 시간대가 UTC임을 보장함으로써 뷰어의 현재 시간대로 쉽게 변환할 수 있도록 합니다. getCreatedAt()에서는 시간을 애플리케이션의 현재 시간대에 맞는 형식의 문자열로 변환합니다.

꽤 간단하지만, 이 예시들은 엔티티 클래스를 사용하는 것이 비즈니스 로직을 강제하고 사용하기 좋은 객체를 만드는 매우 유연한 방법을 제공할 수 있음을 보여줍니다.

<?php

// Auto-hash the password - both do the same thing
$user->password = 'my great password';
$user->setPassword('my great password');

특수 Getter/Setter

Added in version 4.4.0.

예를 들어, 엔티티의 부모 클래스에 이미 getParent() 메서드가 정의되어 있고, 엔티티에도 parent라는 컬럼이 있을 때, 엔티티 클래스의 getParent() 메서드에 비즈니스 로직을 추가하려 하면 해당 메서드가 이미 정의되어 있습니다.

그런 경우에는 특수 getter/setter를 사용할 수 있습니다. getX()/setX() 대신 _getX()/_setX()를 설정하세요.

위 예시에서, 엔티티에 _getParent() 메서드가 있으면 $entity->parent를 가져올 때 해당 메서드가 사용되고, _setParent() 메서드는 $entity->parent를 설정할 때 사용됩니다.

데이터 매핑

경력의 많은 시점에서, 애플리케이션의 사용 방식이 변경되어 데이터베이스의 원래 컬럼 이름이 더 이상 적합하지 않은 상황을 맞닥뜨릴 것입니다. 또는 코딩 스타일이 camelCase 클래스 속성을 선호하지만 데이터베이스 스키마는 snake_case 이름을 요구하는 경우도 있습니다. 이러한 상황은 엔티티 클래스의 데이터 매핑 기능으로 쉽게 처리할 수 있습니다.

예를 들어, 애플리케이션 전체에서 사용되는 단순화된 User 엔티티가 있다고 상상해보세요:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $attributes = [
        'id'         => null,
        'name'       => null, // Represents a username
        'email'      => null,
        'password'   => null,
        'created_at' => null,
        'updated_at' => null,
    ];
}

상사가 와서 더 이상 사용자 이름을 사용하지 않으므로 로그인에 이메일만 사용하도록 전환한다고 합니다. 하지만 애플리케이션을 약간 개인화하고 싶어서, name 필드가 현재처럼 사용자 이름이 아닌 사용자의 전체 이름을 나타내도록 변경해 달라고 합니다. 데이터베이스에서 계속 의미가 통하도록 명확성을 위해 name 필드를 full_name으로 이름을 바꾸는 마이그레이션을 작성합니다.

이 예시가 다소 억지스럽더라도, User 클래스를 수정하는 두 가지 선택지가 있습니다. 클래스 속성을 $name에서 $full_name으로 수정할 수 있지만 그러면 애플리케이션 전체에 변경이 필요합니다. 대신, 데이터베이스의 full_name 컬럼을 $name 속성에 간단히 매핑하면 엔티티 변경을 마칠 수 있습니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $attributes = [
        'id'         => null,
        'full_name'  => null, // In the $attributes, the key is the db column name
        'email'      => null,
        'password'   => null,
        'created_at' => null,
        'updated_at' => null,
    ];

    protected $datamap = [
        // property_name => db_column_name
        'name' => 'full_name',
    ];
}

$datamap 배열에 새 데이터베이스 이름을 추가함으로써, 데이터베이스 컬럼이 어떤 클래스 속성을 통해 접근 가능해야 하는지 클래스에 알릴 수 있습니다. 배열의 키는 매핑할 클래스 속성이고, 배열의 값은 데이터베이스의 컬럼 이름입니다.

이 예시에서, 모델이 User 클래스의 full_name 필드를 설정할 때 실제로는 그 값을 클래스의 $name 속성에 할당하므로 $user->name을 통해 설정하고 가져올 수 있습니다. 값은 모델이 데이터를 다시 가져와 데이터베이스에 저장하는 데 필요하기 때문에, 원래의 $user->full_name을 통해서도 여전히 접근할 수 있습니다. 하지만 unset()isset()은 데이터베이스 컬럼 이름인 $user->full_name이 아닌 매핑된 속성 $user->name에서만 작동합니다.

참고

데이터 매핑을 사용할 때는 데이터베이스 컬럼 이름에 대한 set*()get*() 메서드를 정의해야 합니다. 이 예시에서는 setFullName()getFullName()을 정의해야 합니다.

뮤테이터

날짜 뮤테이터

기본적으로, 엔티티 클래스는 created_at, updated_at, deleted_at이라는 이름의 필드를 설정하거나 가져올 때마다 Time 인스턴스로 변환합니다. Time 클래스는 불변적이고 지역화된 방식으로 많은 유용한 메서드를 제공합니다.

$dates 속성에 이름을 추가하여 자동으로 변환될 속성을 정의할 수 있습니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];
}

이제 해당 속성 중 하나가 설정되면 app/Config/App.php에 설정된 애플리케이션의 현재 시간대를 사용하여 Time 인스턴스로 변환됩니다:

<?php

$user = new \App\Entities\User();

// Converted to Time instance
$user->created_at = 'April 15, 2017 10:30:00';

// Can now use any Time methods:
echo $user->created_at->humanize();
echo $user->created_at->setTimezone('Europe/London')->toDateString();

속성 캐스팅

$casts 속성을 사용하여 엔티티의 속성을 일반적인 데이터 타입으로 변환하도록 지정할 수 있습니다. 이 옵션은 키가 클래스 속성의 이름이고, 값이 캐스팅될 데이터 타입인 배열이어야 합니다.

속성 캐스팅은 읽기(get)와 쓰기(set) 모두에 영향을 미치지만, 일부 타입은 읽기(get)에만 영향을 줍니다.

스칼라 타입 캐스팅

속성은 다음 데이터 타입 중 하나로 캐스팅할 수 있습니다: integer, float, double, string, boolean, object, array, datetime, timestamp, uri, int-bool, enum. 타입 앞에 물음표를 추가하여 속성을 nullable로 표시할 수 있습니다, 예: ?string, ?integer.

참고

int-bool은 v4.3.0부터 사용할 수 있습니다.

참고

enum은 v4.7.0부터 사용할 수 있습니다.

예를 들어, is_banned 속성을 가진 User 엔티티가 있다면 boolean으로 캐스팅할 수 있습니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $casts = [
        'is_banned'          => 'boolean',
        'is_banned_nullable' => '?boolean',
    ];
}

Array/Json 캐스팅

Array/Json 캐스팅은 직렬화된 배열이나 json을 저장하는 필드에 특히 유용합니다. 다음으로 캐스팅할 때:

  • array로 캐스팅하면 자동으로 역직렬화됩니다,

  • json으로 캐스팅하면 자동으로 json_decode($value, false)의 값으로 설정됩니다,

  • json-array로 캐스팅하면 자동으로 json_decode($value, true)의 값으로 설정됩니다,

속성의 값을 설정할 때 위와 같이 처리됩니다. 속성을 캐스팅할 수 있는 나머지 데이터 타입들과 달리, 다음은:

  • array 캐스트 타입은 직렬화하고,

  • jsonjson-array 캐스트는 다음에 json_encode 함수를 사용합니다

속성이 설정될 때마다 해당 값에:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $casts = [
        'options'        => 'array',
        'options_object' => 'json',
        'options_array'  => 'json-array',
    ];
}
<?php

$user    = $userModel->find(15);
$options = $user->options;

$options['foo'] = 'bar';

$user->options = $options;
$userModel->save($user);

CSV 캐스팅

단순한 값들의 평면 배열이 있다는 것을 알고 있다면, 직렬화된 문자열이나 JSON 문자열로 인코딩하는 것이 원래 구조보다 더 복잡할 수 있습니다. 쉼표로 구분된 값(CSV)으로 캐스팅하는 것은 더 간단한 대안으로, 공간을 덜 사용하고 사람이 더 쉽게 읽을 수 있는 문자열을 생성합니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class Widget extends Entity
{
    protected $casts = [
        'colors' => 'csv',
    ];
}

데이터베이스에 “red,yellow,green”으로 저장됩니다:

<?php

$widget->colors = ['red', 'yellow', 'green'];

참고

CSV로 캐스팅하면 PHP 내부의 implodeexplode 메서드를 사용하며, 모든 값이 문자열로 안전하고 쉼표가 없다고 가정합니다. 더 복잡한 데이터 캐스트에는 array 또는 json을 사용하세요.

Enum 캐스팅

Added in version 4.7.0.

속성을 PHP enum으로 캐스팅할 수 있습니다. 파라미터로 enum 클래스 이름을 지정해야 합니다.

Enum 캐스팅은 다음을 지원합니다:

  • Backed enum (string 또는 int) - 백킹 값이 데이터베이스에 저장됩니다

  • Unit enum - 케이스 이름이 문자열로 데이터베이스에 저장됩니다

예를 들어, backed enum을 사용하는 status 속성을 가진 User 엔티티가 있다면:

<?php

namespace App\Enums;

enum UserStatus: string
{
    case Active   = 'active';
    case Inactive = 'inactive';
    case Pending  = 'pending';
}

엔티티에서 다음과 같이 캐스팅할 수 있습니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $casts = [
        'status' => 'enum[App\Enums\UserStatus]',
    ];
}

이제 status 속성에 접근하면 자동으로 UserStatus enum 인스턴스로 변환됩니다:

<?php

use App\Enums\UserStatus;

$user = $userModel->find(1);

// Returns a UserStatus enum instance
echo $user->status->value; // 'active'

// Set using enum
$user->status = UserStatus::Inactive;

// Or set using the backing value (will be converted to enum on read)
$user->status = 'pending';

// Note: Internally, enums are always stored as their backing value (string/int)
// in the entity's $attributes array

nullable enum의 경우:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    protected $casts = [
        'status' => '?enum[App\Enums\UserStatus]',
    ];
}

커스텀 캐스팅

데이터를 가져오고 설정하기 위한 자체 변환 타입을 정의할 수 있습니다.

먼저 타입에 대한 핸들러 클래스를 생성해야 합니다. 클래스가 app/Entities/Cast 디렉터리에 위치한다고 가정합니다:

<?php

namespace App\Entities\Cast;

use CodeIgniter\Entity\Cast\BaseCast;

// The class must inherit the CodeIgniter\Entity\Cast\BaseCast class
class CastBase64 extends BaseCast
{
    public static function get($value, array $params = [])
    {
        return base64_decode($value, true);
    }

    public static function set($value, array $params = [])
    {
        return base64_encode($value);
    }
}

이제 등록해야 합니다:

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class MyEntity extends Entity
{
    // Specify the type for the field
    protected $casts = [
        'key' => 'base64',
    ];

    // Bind the type to the handler
    protected $castHandlers = [
        'base64' => Cast\CastBase64::class,
    ];
}

// ...

$entity->key = 'test'; // dGVzdA==
echo $entity->key;     // test

값을 가져오거나 설정할 때 값을 변경할 필요가 없다면, 해당 메서드를 구현하지 않으면 됩니다:

<?php

namespace App\Entities\Cast;

use CodeIgniter\Entity\Cast\BaseCast;

class CastBase64 extends BaseCast
{
    public static function get($value, array $params = [])
    {
        return base64_decode($value, true);
    }
}

파라미터

경우에 따라 하나의 타입으로는 충분하지 않을 수 있습니다. 이런 상황에서는 추가 파라미터를 사용할 수 있습니다. 추가 파라미터는 대괄호 안에 type[param1, param2]처럼 쉼표로 구분하여 나열합니다.

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class MyEntity extends Entity
{
    // Define a type with parameters
    protected $casts = [
        'some_attribute' => 'class[App\SomeClass, param2, param3]',
    ];

    // Bind the type to the handler
    protected $castHandlers = [
        'class' => 'SomeHandler',
    ];
}
<?php

namespace App\Entities\Cast;

use CodeIgniter\Entity\Cast\BaseCast;

class SomeHandler extends BaseCast
{
    public static function get($value, array $params = [])
    {
        var_dump($params);
        /*
         * Output:
         * array(3) {
         *   [0]=>
         *   string(13) "App\SomeClass"
         *   [1]=>
         *   string(6) "param2"
         *   [2]=>
         *   string(6) "param3"
         * }
         */
    }
}

참고

캐스팅 타입이 ?bool처럼 nullable로 표시되고 전달된 값이 null이 아닌 경우, nullable 값을 가진 파라미터가 캐스팅 타입 핸들러에 전달됩니다. 캐스팅 타입에 미리 정의된 파라미터가 있으면 nullable은 목록의 끝에 추가됩니다.

변경된 속성 확인하기

엔티티 속성이 생성된 이후 변경되었는지 확인할 수 있습니다. 유일한 파라미터는 확인할 속성의 이름입니다:

<?php

$user = new \App\Entities\User();
$user->hasChanged('name'); // false

$user->name = 'Fred';
$user->hasChanged('name'); // true

또는 전체 엔티티에서 변경된 값을 확인하려면 파라미터를 생략합니다:

<?php

$user->hasChanged(); // true

깊은 변경 추적

Added in version 4.7.0.

엔티티 클래스는 객체와 배열에 대해 깊은 비교를 수행하여 내부 상태의 변경을 정확하게 감지합니다.

스칼라 값

스칼라 값(문자열, 정수, 부동소수점, 불리언, null)의 경우 엔티티는 직접 비교를 사용합니다. 엔티티의 모든 속성이 스칼라인 경우 더 나은 성능을 위해 최적화된 비교가 사용됩니다.

객체와 배열

객체와 배열의 경우, 엔티티는 비교를 위해 값을 JSON으로 인코딩하고 정규화합니다. 이는 중첩된 구조, 객체 속성, 배열 요소, 중첩된 엔티티(toRawArray() 사용), enum(BackedEnumUnitEnum), datetime 객체(DateTimeInterface), 컬렉션(Traversable), __toString()을 가진 값 객체, 그리고 JsonSerializable 또는 toArray()를 구현한 객체에 대한 수정이 올바르게 감지됨을 의미합니다.