44
Примеры решения типичных задач за рамками ядра Yii2 QuartSoft YiiSoft Климов П.В.

Примеры решения типичных задач за рамками ядра Yii2

Embed Size (px)

Citation preview

Page 1: Примеры решения типичных задач за рамками ядра Yii2

Примеры

решения

типичных задач

за рамками ядра

Yii2

QuartSoft

YiiSoft

Климов П.В.

Page 2: Примеры решения типичных задач за рамками ядра Yii2

Интернационализация статического

текста

I18N

$translations

translate()

“translation

source”

1

*MessageSource

translate()DbMessageSource

PhpMessageSource

Client

“Yii::t()”

Page 3: Примеры решения типичных задач за рамками ядра Yii2

Интернационализация сущностей в

базе данных

Itemn

id

canonicalName

price

Language

id

name

locale

Translation

n

itemId

languageId

name

description

Page 4: Примеры решения типичных задач за рамками ядра Yii2

Расширение «yii2tech/ar-variation»

VariationBehavior

$variationRelation

$defaultVariationRelation

“attached behavior”

1

*

Item

$canonicalName

Language

ItemTranslation

$name

1

1

1

* “has many”

“has many”

“has many via”

Page 5: Примеры решения типичных задач за рамками ядра Yii2

class Item extends \yii\db\ActiveRecord

{

public function behaviors()

{

return [

‘translations’ => [

'class' => VariationBehavior::class,

'variationsRelation' => 'translations',

'variationOptionReferenceAttribute' => 'languageId',

'optionModelClass' => Language::class,

]

];

}

public function getTranslations()

{

return $this->hasMany(ItemTranslation::class, [‘itemId' => 'id']);

}

}

Конфигурация «VariationBehavior»

Page 6: Примеры решения типичных задач за рамками ядра Yii2

$model = new Item();

// `getVariationModels()` возвращает список моделей-вариаций

$variations = $model->getVariationModels();

// их количество всегда равно количеству доступных опций:

var_dump(count($variations) == Language::find()->count()); // `true`

// валидация и сохранение производятся автоматически:

$post = Yii::$app->request->post();

if ($model->load($post)

&& Model::loadMultiple($model->getVariationModels(), $post)

&& $model->save())

{

return $this->redirect(['index']);

}

Управление вариаторами

Page 7: Примеры решения типичных задач за рамками ядра Yii2

class Item extends \yii\db\ActiveRecord

{

public function behaviors()

{

return [

‘translations’ => [

// …

'defaultVariationRelation' => 'defaultTranslation',

'defaultVariationOptionReference' => function () {

return Yii::$app->language; // ID опции по умолчанию

},

'variationAttributeDefaultValueMap' => [

‘name' => ‘canonicalName‘ // fallback для атрибутов вариатора

],

]

];

}

public function getDefaultTranslation()

{

return $this->hasDefaultVariationRelation(); // `has many` -> `has one`

}

}

Вариация «по умолчанию»

Page 8: Примеры решения типичных задач за рамками ядра Yii2

class VariationBehavior extends \yii\base\Behavior

{

public function __get($name)

{

try {

return parent::__get($name);

} catch (\yii\base\UnknownPropertyException $e) {

return $this->getDefaultVariationModel()->{$name};

}

}

public function __set($name, $value) {…}

public function canGetProperty($name, $checkVars = true) {…}

public function canSetProperty($name, $checkVars = true) {…}

}

Добавление виртуальных свойств

«хозяину» поведения

Page 9: Примеры решения типичных задач за рамками ядра Yii2

$model = Item::find()->one();

echo $model->name; // вернет `$model->defaultTranslation->name`

// а если `defaultTranslation` не найдено, то `$model->canonicalName`

// Задание нового значения:

$model->name = ‘New translation name’;

// «Жадная» загрузка:

$models = Item::find()->with(‘defaultTranslation’)->all();

foreach ($models as $model) {

echo $model->name;

}

Прямой доступ к атрибутам вариатора

Page 10: Примеры решения типичных задач за рамками ядра Yii2

Сущности с общими атрибутами

Преподаватель Студент

- Ученая

степень

- Зарплата

- ФИО

- Паспорт

- Адрес

- Телефон

- Учебная

группа

- Стипендия

Page 11: Примеры решения типичных задач за рамками ядра Yii2

Роли в реляционной базе данных

Person

id

name

address

phone Student

personId

studyGroupId

scolarship

Instructor

personId

rankId

salary

Page 12: Примеры решения типичных задач за рамками ядра Yii2

Роли в реляционной базе данных

Person

1

id

name

address

phone Student

personId

studyGroupId

scolarship

Can be

1

Instructor

personId

rankId

salary

Can be1 1

Page 13: Примеры решения типичных задач за рамками ядра Yii2

Расширение «yii2tech/ar-role»

RoleBehavior

$roleRelation

“attached behavior”

1

Student

Instructor

Person

1

1

1“has one”

“has one”

1

1

“attached behavior”

1

1

Page 14: Примеры решения типичных задач за рамками ядра Yii2

class Student extends \yii\db\ActiveRecord

{

public function behaviors()

{

return [

‘translations’ => [

'class' => RoleBehavior::class,

‘roleRelation' => ‘person',

]

];

}

public function getPerson()

{

return $this->hasOne(Person::class, [‘id' => ‘personId']);

}

}

Конфигурация «RoleBehavior»

Page 15: Примеры решения типичных задач за рамками ядра Yii2

class RoleBehavior extends \yii\base\Behavior

{

public function __get($name)

{

try {

return parent::__get($name);

} catch (\yii\base\UnknownPropertyException $e) {

return $this->getRoleRelationModel()->{$name};

}

}

public function __call($name, $params)

{

$model = $this->getRoleRelationModel();

if ($model->hasMethod($name)) {

return call_user_func_array([$model, $name], $params);

}

return parent::__call($name, $params);

}

}

Эмуляция наследования

Page 16: Примеры решения типичных задач за рамками ядра Yii2

$model = Student::find()->one();

echo $model->name; // вернет `$model->person->name`

// Задание нового значения:

$model->name = ‘John Doe’;

// валидация и сохранение производятся автоматически:

$model->save(); // вызов `$model->person->save()`

$model->locateAddress(); // вызов `$model->person->locateAddress()`

// «Жадная» загрузка:

$models = Student::find()->with(‘person’)->all();

foreach ($models as $model) {

echo $model->name;

}

Прямой доступ к атрибутам роли

Page 17: Примеры решения типичных задач за рамками ядра Yii2

Сохранение файлов

Client

Web

Server

HTTP

requests

HDD

File read/write

Page 18: Примеры решения типичных задач за рамками ядра Yii2

Распределенные приложения

Client

Load

Balancer

Regular

HTTP

requests

Web

Server 1

Web

Server 2

Internal HTTP requests

HDD 1 HDD 2

File read/write File read/write

Page 19: Примеры решения типичных задач за рамками ядра Yii2

Централизованное файловое хранилище

Client

Load

Balancer

Regular

HTTP

requests

Web

Server 1

Web

Server 2

Internal HTTP requests

File

ServerFTP / SFTP / REST

FTP / SFTP / REST

Page 20: Примеры решения типичных задач за рамками ядра Yii2

Абстракция файловой системы

sftp\Storage

file\Storage

$buckets

getBucket()

“consists of”

1

*file\Bucket

saveFileContent()

getFileContent()

local\Storage

sftp\Bucket

local\Bucket

Client

“file

r/w”

Page 21: Примеры решения типичных задач за рамками ядра Yii2

$bucket = Yii::$app->fileStorage->getBucket('tempFiles');

// создать файл с заданным содержимым:

$bucket->saveFileContent('foo.txt', 'Foo content');

// удалить файл:

$bucket->deleteFile('foo.txt');

// скопировать файл в хранилище:

$bucket->copyFileIn('/path/to/source/file.txt', 'file.txt');

// скопировать файл из хранилища:

$bucket->copyFileOut('file.txt', '/path/to/destination/file.txt');

var_dump($bucket->fileExists('file.txt')); // выводит `true`

echo $bucket->getFileUrl('file.txt'); // выводит:

// http://domain.com/files/f/i/file.txt'

Операции с файлами

Page 22: Примеры решения типичных задач за рамками ядра Yii2

Файловые потоки

// Открыть локальный файл:

$resource = fopen(‘/path/to/file.txt’, ‘r’);

// Открыть удаленный файл по протоколу HTTP:

// файл загружается по протоколу HTTP и доступен как локальный:

$resource = fopen(‘http://example.com/file.txt’, ‘r’);

// Дескриптор потока в общем виде:

$resource = fopen(‘{PROTOCOL}://{PATH}[?{QUERY}]’, ‘r’);

Page 23: Примеры решения типичных задач за рамками ядра Yii2

Потоковая Обвертка

class StreamWrapper

{

public function stream_open($path, $mode, $options, &$openedPath) {}

public function stream_close() {}

public function stream_eof() {}

public function stream_read($count) {}

public function stream_write($data) {}

}

stream_wrapper_register(‘foo’, StreamWrapper::class, STREAM_IS_URL);

Page 24: Примеры решения типичных задач за рамками ядра Yii2

Примеры

// Потоковая обвертка для протокола HTTP:

$resource = fopen(‘http://example.com/file.txt’, ‘r’);

// Потоковая обвертка для протокола FTP:

$resource = fopen(‘ftp://user:[email protected]/file.txt’, ‘r’);

// Потоковая обвертка для протокола SFTP:

$resource = fopen(‘ssh2.sftp://1234file.txt’, ‘r’);

// Потоковая обвертка для MongoDB GridFS:

$resource = fopen(‘gridfs://mydatabase.fs?filename=file.txt’, ‘r’);

// Потоковая обвертка для Amazon S3:

$resource = fopen(‘s3://bucket-name/file.txt’, ‘r’);

Page 25: Примеры решения типичных задач за рамками ядра Yii2

$bucket = Yii::$app->fileStorage->getBucket('tempFiles');

// открытие дескриптора потока:

$resource = $bucket->openFile(‘file.dat’, ‘r’);

while (!feof($resource)) {

echo fread($resource, 1024);

}

fclose($resource);

Обработка больших файлов

Page 26: Примеры решения типичных задач за рамками ядра Yii2

Максимальное количество файлов в

одном каталоге

• Для «старых» файловых систем (FAT) –

65 000

• Для современных (NTFS) теоретическое

– 4 294 967 295

• Практическое (без существенной потери

производительности) – 50 000..100 000

Page 27: Примеры решения типичных задач за рамками ядра Yii2

Шаблон под-каталога

return [

'components' => [

'fileStorage' => [

'class' => 'yii2tech\filestorage\local\Storage',

'buckets' => [

‘item' => [

'fileSubDirTemplate' => '{^name}/{^^name}',

],

]

// ...

];

$bucket = Yii::$app->fileStorage->getBucket(‘item');

$bucket->saveFileContent('foo.txt', 'Foo content');

// реальное имя файла - ‘f/o/foo.txt’

Page 28: Примеры решения типичных задач за рамками ядра Yii2

Связывание файлов с сущностями БД

Item

id

fileExtension

fileVersion

Имя файла

Сохранение

mime-типа

Чтобы

обмануть кэш

браузера

Page 29: Примеры решения типичных задач за рамками ядра Yii2

Расширение «yii2tech/ar-file»

"yii2tech/file-storage"

FileBehavior

“attached behavior”

Item

1

1file\Storage

file\Bucket

“consists of”

1

*

$fileAttribute

“read / write

file”"yii2tech/ar-file"

Page 30: Примеры решения типичных задач за рамками ядра Yii2

class Item extends \yii\db\ActiveRecord

{

public function behaviors()

{

return [

‘file’ => [

'class' => FileBehavior::class,

‘fileStorageBucket' => ‘item',

‘fileExtensionAttribute' => ‘fileExtension',

‘fileVersionAttribute' => ‘fileVersion',

]

];

}

}

Конфигурация «FileBehavior»

Page 31: Примеры решения типичных задач за рамками ядра Yii2

use yii\web\UploadedFile;

$model = Item::findOne(1);

$model->file = UploadedFile::getInstance($model, 'file');

$model->save();

var_dump($model->fileExists()); // выводит `true`

Сохранение файлов

Page 32: Примеры решения типичных задач за рамками ядра Yii2

{

“name”: “John Doe”,

“email”: “[email protected]”,

“address” : {

“city”: “Houston”,

“region”: “Texas”,

},

“comments”: [

{

“date”: “2016-11-26 08:16:11”,

“content”: “Hello World”

},

]

}

Комплексные записи

Page 33: Примеры решения типичных задач за рамками ядра Yii2

Вложенные модели

User

$name

$email

1“has address”

1

1

Address

$city

$region

Comment

$date

$content

mongodb\ActiveRecord

“has comments”

*

Model

Page 34: Примеры решения типичных задач за рамками ядра Yii2

Расширение «yii2tech/embedded»

"yii2tech/embedded"

User

1

ContainerTrait

*

Container

Interface

Mapping

Address

“Declare and

store”

Comment

“Satisfy

interface”

Page 35: Примеры решения типичных задач за рамками ядра Yii2

class User extends ActiveRecord implements ContainerInterface

{

use ContainerTrait;

public function embedAddressModel()

{

return $this->mapEmbedded(‘address', Address::class);

}

public function embedCommentModels()

{

return $this->mapEmbeddedList('comments', Comment::class);

}

}

Объявление вложенных объектов

Page 36: Примеры решения типичных задач за рамками ядра Yii2

$user = new User();

// Заполнение одиночного вложенного объекта:

$user->addressModel->city = ‘Houston';

$user->addressModel->region = ‘Texas';

// Заполнение списка:

$comment = new Comment();

$comment->content = ‘New comment’;

$user->comments[] = $comment;

// Синхронизация данных:

$user->refreshFromEmbedded();

var_dump($user->address); // выводит массив:

// ['city' => 'Houston', 'region' => 'Texas']

Доступ к вложенным объектам

Page 37: Примеры решения типичных задач за рамками ядра Yii2

События и поведения

Component

trigger()

attachBehavior()

$events

$behaviors

Behavior

events()

$owner

1*

Event

$sender

1 1

* *“Trigger” “Handle”

Handler

PHP

Callback

*

1

*

1

“Handle event” “Declare event handler”

1 *

“Has behavior”

“Has owner”

Page 38: Примеры решения типичных задач за рамками ядра Yii2

class User extends \yii\db\ActiveRecord

{

public function behaviors()

{

return [

‘timestamp’ => [

'class' => TimestampBehavior::class,

// обрабатывает только 2 собыития:

// - `beforeInsert`

// - `beforeUpdate`

]

];

}

}

Конфигурация «TimestampBehavior»

Page 39: Примеры решения типичных задач за рамками ядра Yii2

$user = new User(); // срабатывает событие `init`

// поведения проинициализированы!

$models = User::find()->all(); // срабатывает событие `afterFind`

// поведения проинициализированы!

unset($models); // объекты НЕ разрушены!

gc_collect_cycles(); // принудительная сборка мусора

// только теперь объекты разрушены

Инициализация и разрушение

поведений

Page 40: Примеры решения типичных задач за рамками ядра Yii2

class User extends \yii\db\ActiveRecord

{

public function init()

{

// «прыжок» через родителя

// убираем событие «init»

\yii\base\Model::init();

}

public function afterFind()

{

// нет вызова родительской реализации

// убираем событие «afterFind»

}

}

Устранение «лишних» событий

Page 41: Примеры решения типичных задач за рамками ядра Yii2

$user = new User(); // нет события `init`

// поведения НЕ проинициализированы!

$models = User::find()->all(); // нет события `afterFind`

// поведения НЕ проинициализированы!

unset($models); // объекты разрушены!

echo gc_collect_cycles(); // выводит `0`

$user->save(); // событие `beforeInsert`

// поведения проинициализированы!

Отложенная инициализация

поведений

Page 42: Примеры решения типичных задач за рамками ядра Yii2

Расширение «yii2tech/behavior-trait»

User BehaviorTrait

ActiveRecord

“Allow usage of

inline methods as

event handlers”

TimestampTrait

“Add particular event

handler”

Page 43: Примеры решения типичных задач за рамками ядра Yii2

class User extends \yii\db\ActiveRecord

{

use BehaviorTrait;

use TimestampTrait;

}

trait TimestampTrait

{

// Имя обработчика: «{событие}Handler{суффикс}»

// Обработка события «beforeInsert»

public function beforeInsertHandlerTimestamp($event)

{

$this->createdAt = time();

}

}

Использование «BehaviorTrait»

Page 44: Примеры решения типичных задач за рамками ядра Yii2

Примеры решения типичных задач за

рамками ядра Yii2

• Интернационализация в БД

• Роли в реляционных БД

• Абстракция файлового хранилища

• Связывание файлов с записями в БД

• Вложенные модели

• Trait вместо Behavior

http://www.yiiframework.com/

https://github.com/yii2tech