Entity 클래스 사용

CodeIgniter는 데이터베이스 계층에서 Entity 클래스를 1급 시민으로 지원하며, 완전히 선택적으로 사용할 수 있도록 유지합니다. 일반적으로 리포지토리 패턴의 일부로 사용되지만 필요에 따라 모델과 함께 직접 사용할 수 있습니다.

Entity 사용법

기본적으로 Entity 클래스는 단일 데이터베이스 행을 나타내는 클래스입니다. 여기에는 데이터베이스 열을 나타내는 클래스 속성이 있으며, 해당 행에 대한 비즈니스 논리를 구현하기 위한 추가 방법을 제공합니다. 여기에서 핵심은 지속 방법에 대해 전혀 모른다는 것입니다. 지속방법에 대해서는 모델 또는 저장소 클래스의 책임입니다. 이렇게하면 객체 저장 방법에 변화가 생길 경우 어플리케이션 전체에서 객체가 사용되는 방식을 변경할 필요가 없습니다. 따라서 빠른 프로토 타이핑 단계에서 JSON 또는 XML 파일을 사용하여 객체를 저장한 다음 개념이 작동하는 것이 입증되면 데이터베이스로 쉽게 전환할 수 있습니다.

매우 간단한 User Entity를 살펴보고이를 명확하게하는 데 도움이되는 방법을 살펴 보겠습니다.

다음 스키마를 가진 users라는 데이터베이스 테이블이 있다고 가정하십시오.

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

Important

attributes은 내부 사용을 위한 예약어입니다. 컬럼명으로 사용하면 Entity가 제대로 동작하지 않습니다.

Entity 클래스 만들기

이제 새 엔티티 클래스를 작성합니다. 이러한 클래스를 저장할 기본 위치는 없으며, 기존 디렉토리 구조와 맞지 않기 때문에 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 필드는 클래스 또는 데이터베이스에서 자동으로 처리되므로 변경하지 않습니다. 마지막으로 Entity 클래스를 $returnType으로 설정했습니다. 이를 통해 데이터베이스에서 행을 반환하는 모델의 모든 메소드가 일반 객체나 배열 대신 User Entity 클래스의 인스턴스를 반환합니다.

Entity 클래스 작업

이제 모든 조각이 제자리에 배치되었으므로 다른 클래스와 마찬가지로 Entity 클래스로 작업합니다.

<?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()으로 속성을 확인하는 기능을 제공합니다.

Note

Entity 클래스는 데이터를 $attributes 속성에 저장합니다.

User가 모델의 save() 메소드로 전달되면 자동으로 특성을 읽고 모델의 $allowedFields 속성에 나열된 열의 변경 사항을 저장합니다. 또한 새 행을 만들거나 기존 행을 업데이트할지 여부도 알고 있습니다.

Note

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

빠르게 속성 채우기

Entity 클래스는 키/값 쌍 배열을 클래스에 전달하여 클래스 속성을 채울 수 있는 fill() 메소드도 제공합니다. 배열의 모든 속성은 Entity에 설정됩니다. 그러나 모델을 통해 저장할 때 $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);

대량 액세스 속성

Entity 클래스는 toArray()toRawArray() 메소드를 통하여 사용 가능한 모든 속성을 배열로 추출할 수 있습니다. 원시(raw) 버전을 사용하면 매직 “getter” 메소드와 캐스트(cast)를 우회할 수 있습니다. 두 메소드 모두 첫 번째 매개 변수를 사용하여 반환된 값을 변경된 값으로 필터링할지 여부를 지정하고, 최종 매개 변수를 사용하여 중첩된 엔티티 요소를 재귀적으로 만들수 있습니다.

비즈니스 로직 처리

위의 예제는 편리하지만 비즈니스 로직을 강화하는데 도움이 되지는 않습니다. 기본 Entity 클래스는 특수한 메소드를 확인하고 속성을 직접 사용하는 대신 스마트한 __get()__set() 메소드를 구현하여 비즈니스 로직 또는 데이터 변환을 시행할 수 있습니다.

다음은 이를 사용하는 방법에 대한 몇 가지 예를 제공하기 위해 업데이트된 사용자 Entity입니다.

<?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로 작성된 컬럼 이름을 set 또는 get 접두사가 붙은 PascalCase로 변환합니다. 이 메소드는 직접 구문을 (예: $user->email) 사용하여 클래스 속성을 설정하거나 검색할 때마다 자동으로 호출됩니다. 다른 클래스에서 액세스하지 않으려면 메소드를 공개(public)하지 않아도됩니다. 예를 들어, created_at 클래스 속성은 setCreatedAt()getCreatedAt() 메소드를 통해 액세스됩니다.

Note

이 방법은 클래스 외부에서 속성에 액세스하려고 할 때만 작동합니다. 클래스 내부의 모든 메소드는 setX()getX() 메소드를 직접 호출해야 합니다.

setPassword() 메소드는 비밀번호가 항상 해시되도록 합니다.

setCreatedAt() 메소드는 모델에서 받은 문자열을 DateTime 객체로 변환하여, 시간대가 UTC인지 확인하여 뷰어의 현재 시간대를 쉽게 변환합니다. getCreatedAt() 메소드는 시간을 어플리케이션의 사용중인 시간대의 지정된 형식 문자열로 변환합니다.

이 예제는 상당히 간단하지만 Entity 클래스를 사용하여 비즈니스 로직 적용과 사용하기 편리한 객체를 만드는 매우 유연한 방법을 제공합니다.

<?php

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

데이타 매핑

어플리케이션 개발중 기획이 변경되어 데이터베이스의 컬럼 이름이 더 이상 타당하지 않는 상황이 발생하거나, 코딩 스타일이 camelCase 클래스 특성을 선호하지만 데이터베이스 스키마에 snake_case 이름이 필요하다는 것을 깨닫게되는 경우도 있습니다. 이러한 상황은 Entity 클래스의 데이터 매핑 기능으로 쉽게 처리할 수 있습니다.

다음 예처럼 어플리케이션 전체에서 사용되는 단순화된 사용자 Entity가 있다고 가정합니다.

<?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 속성에 매핑하고 Entity 변경을 수행합니다.

<?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 배열에 추가하면 데이터베이스 컬럼에 액세스할 수 있는 클래스 속성을 클래스에 알릴 수 있습니다. 배열의 키는 데이터베이스의 컬럼 이름이며, 배열의 값은 이를 맵핑할 클래스 속성입니다.

이 예에서는 모델이 사용자 클래스에서 full_name 필드를 설정할 때 실제로 해당 값을 클래스의 $name 속성에 할당하여 $user->name을 통해 설정하고 검색할 수 있습니다. The value will still be accessible through the original $user->full_name, also, as this is needed for the model to get the data back out and save it to the database. 모델이 데이터를 가져 와서 데이터베이스에 저장하는데 필요하기 때문에 $user->full_name을 통해 값에 계속 액세스할 수 있습니다. 그러나 unsetisset은 원래 이름인 full_name이 아닌 매핑된 속성 $name에서만 작동합니다.

Note

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

뮤테이터(Mutators)

데이타 뮤테이터

기본적으로 Entity 클래스는 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 속성을 사용하여 엔티티의 속성을 공통 데이터 유형으로 변환하도록 지정할 수 있습니다. 이 옵션은 키가 클래스 속성의 이름이고 값은 캐스트해야 하는 데이터 유형인 배열이어야합니다. 캐스팅은 값을 읽을 때만 영향을 줍니다. 엔티티나 데이터베이스의 영구적인 값에 영향을 주는 변환이 발생하지 않습니다. 속성은 다음 데이터 형식중 하나로 캐스팅할 수 있습니다: integer, float, double, string, boolean, object, array, datetime, timestamp, uri. 유형의 시작 부분에 물음표를 추가하면 특성을 null 입력 가능으로 표시합니다. 예 : ?string, ?integer.

다음 예는 User Entity의 is_banned 속성을 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, 자동으로 직렬화 해제(unserialized)

  • json, json_decode($value, false) 값으로 자동 설정

  • json-array, json_decode($value, true) 값으로 자동 설정

속성 값을 설정할 때 속성을 캐스팅할 수있는 나머지 데이터 형식과 달리:

  • array, serialize 하여 캐스트,

  • 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'];

Note

CSV로 캐스팅은 PHP의 내장 함수 implodeexplode 함수를 사용하며 모든 값이 쉼표가 없는 문자열이라고 가정합니다. 더 복잡한 데이터를 캐스팅하려면 array 또는 json을 사용합니다.

커스텀 캐스팅

데이터를 가져오고 설정하는 고유한 변환 유형을 정의할 수 있습니다.

처음에는 사용자 유형에 대한 처리기 클래스를 만들어야 합니다. 클래스가 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
{
    // Specifying the type for the field
    protected $casts = [
        'key' => 'base64',
    ];

    // Bind the type to the handler
    protected $castHandlers = [
        'base64' => \App\Entities\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);
    }
}

Parameters

한 가지 유형으로 충분하지 않다면, 추가 매개 변수를 사용할 수 있습니다. 추가 매개 변수는 대괄호로 표시되고 쉼표로 나열됩니다.

type[param1, param2]

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class MyEntity extends Entity
{
    // Defining 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"
         * }
         */
    }
}

Note

캐스팅 유형이 nullable ?bool로 표시되어 있고 전달 된 값이 null이 아닌 경우 값이 nullable인 매개 변수가 캐스팅 유형 처리기에 전달됩니다. 캐스팅 유형에 사전 정의된 매개 변수가 있는 경우 목록 끝에 nullable이 추가됩니다.

변경된 속성 확인

속성의 이름을 이용하여 엔티티 속성이 작성된 이후로 변경되었는지 확인할 수 있습니다.

<?php

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

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

전체 엔티티의 변경 여부를 확인하고 싶다면 매개 변수를 생략하십시오.

<?php

$user->hasChanged(); // true