From f34c1c8d3036d5b699775ea462e238292522f314 Mon Sep 17 00:00:00 2001 From: Nekrasov Ilya Date: Thu, 29 Nov 2018 18:09:57 +0300 Subject: [PATCH] Language accessors --- README.md | 29 +++++++++++++++++++++++++ src/Models/ArrayableModel.php | 34 +++++++++++++++++++++++++++-- src/Models/BaseBitrixModel.php | 39 ++++++++++++++++++++++++++++++++++ src/Models/ElementModel.php | 13 ++++++++++++ src/ServiceProvider.php | 2 ++ tests/ElementModelTest.php | 27 ++++++++++++++++++----- tests/ElementQueryTest.php | 4 ++-- tests/Stubs/TestElement.php | 4 +++- 8 files changed, 142 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dab7d9d..1f867df 100644 --- a/README.md +++ b/README.md @@ -353,6 +353,35 @@ $users = Product::fromSectionWithCode('sale')->getList(); protected $appends = ['FULL_NAME']; ``` +#### Языковые аксессоры + +Для многоязычных сайтов типичным является подход, когда для каждого языка создается своё свойство, например, UF_TITLE_RU, UF_TITLE_BY +В этом случае для каждого такого поля можно создать аксессор: +``` +// используем далее $section['UF_TITLE']; +public function getUfTitleAttribute() +{ + return $this['UF_TITLE_' . strtoupper(LANGUAGE_ID)]; +} + +// используем далее $element['PROPERTY_TITLE']; +public function getPropertyTitleAttribute() +{ + return $this['PROPERTY_TITLE_' . strtoupper(LANGUAGE_ID) . '_VALUE']; +} +``` +Так как эти аксессоры однотипны и имеют неприятную особенность засорять модели, то для них можно использовать специальный краткий синтаксис + +``` +class Product extends ElementModel +{ + protected $languageAccessors = [ + 'PROPERTY_TITLE', + 'PROPERTY_FOO' + ]; +} +``` + ### События моделей (Model Events) События позволяют вклиниваться в различные точки жизненного цикла модели и выполнять в них произвольный код. diff --git a/src/Models/ArrayableModel.php b/src/Models/ArrayableModel.php index a1d19dc..4513714 100644 --- a/src/Models/ArrayableModel.php +++ b/src/Models/ArrayableModel.php @@ -40,6 +40,13 @@ abstract class ArrayableModel implements ArrayAccess, Arrayable, Jsonable, Itera * @var array */ protected $appends = []; + + /** + * Array of language fields with auto accessors. + * + * @var array + */ + protected $languageAccessors = []; /** * Array related models indexed by the relation names. @@ -74,7 +81,8 @@ public function offsetSet($offset, $value) */ public function offsetExists($offset) { - return $this->getAccessor($offset) ? true : isset($this->fields[$offset]); + return $this->getAccessor($offset) || $this->getAccessorForLanguageField($offset) + ? true : isset($this->fields[$offset]); } /** @@ -100,8 +108,16 @@ public function offsetGet($offset) { $fieldValue = isset($this->fields[$offset]) ? $this->fields[$offset] : null; $accessor = $this->getAccessor($offset); + if ($accessor) { + return $this->$accessor($fieldValue); + } + + $accessorForLanguageField = $this->getAccessorForLanguageField($offset); + if ($accessorForLanguageField) { + return $this->$accessorForLanguageField($offset); + } - return $accessor ? $this->$accessor($fieldValue) : $fieldValue; + return $fieldValue; } /** @@ -127,6 +143,20 @@ private function getAccessor($field) return method_exists($this, $method) ? $method : false; } + + /** + * Get accessor for language field method name if it exists. + * + * @param string $field + * + * @return string|false + */ + private function getAccessorForLanguageField($field) + { + $method = 'getValueFromLanguageField'; + + return in_array($field, $this->languageAccessors) && method_exists($this, $method) ? $method : false; + } /** * Add value to append. diff --git a/src/Models/BaseBitrixModel.php b/src/Models/BaseBitrixModel.php index c8421f6..15ec17a 100644 --- a/src/Models/BaseBitrixModel.php +++ b/src/Models/BaseBitrixModel.php @@ -11,6 +11,11 @@ abstract class BaseBitrixModel extends ArrayableModel { use ModelEventsTrait; + /** + * @var string|null + */ + protected static $currentLanguage = null; + /** * Array of model fields keys that needs to be saved with next save(). * @@ -455,4 +460,38 @@ public function populateRelation($name, $records) { $this->related[$name] = $records; } + + /** + * Setter for currentLanguage. + * + * @param $language + * @return mixed + */ + public static function setCurrentLanguage($language) + { + self::$currentLanguage = $language; + } + + /** + * Getter for currentLanguage. + * + * @return string + */ + public static function getCurrentLanguage() + { + return self::$currentLanguage; + } + + /** + * Get value from language field according to current language. + * + * @param $field + * @return mixed + */ + protected function getValueFromLanguageField($field) + { + $key = $field . '_' . $this->getCurrentLanguage(); + + return isset($this->fields[$key]) ? $this->fields[$key] : null; + } } diff --git a/src/Models/ElementModel.php b/src/Models/ElementModel.php index c5b3ae1..78e0447 100644 --- a/src/Models/ElementModel.php +++ b/src/Models/ElementModel.php @@ -477,6 +477,19 @@ protected function internalUpdate($fields, $fieldsSelectedForSave) return $result; } + /** + * Get value from language field according to current language. + * + * @param $field + * @return mixed + */ + protected function getValueFromLanguageField($field) + { + $key = $field . '_' . $this->getCurrentLanguage() . '_VALUE'; + + return isset($this->fields[$key]) ? $this->fields[$key] : null; + } + /** * @param $value */ diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 8e14110..3b7d9fa 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -4,6 +4,7 @@ use Arrilot\BitrixBlade\BladeProvider; use Arrilot\BitrixModels\Debug\IlluminateQueryDebugger; +use Arrilot\BitrixModels\Models\BaseBitrixModel; use Arrilot\BitrixModels\Models\EloquentModel; use Bitrix\Main\Config\Configuration; use DB; @@ -23,6 +24,7 @@ class ServiceProvider */ public static function register() { + BaseBitrixModel::setCurrentLanguage(strtoupper(LANGUAGE_ID)); self::bootstrapIlluminatePagination(); } diff --git a/tests/ElementModelTest.php b/tests/ElementModelTest.php index 65f3f07..e917588 100644 --- a/tests/ElementModelTest.php +++ b/tests/ElementModelTest.php @@ -13,6 +13,7 @@ class ElementModelTest extends TestCase public function setUp() { TestElement::$bxObject = m::mock('obj'); + TestElement::setCurrentLanguage('RU'); ElementQuery::$cIblockObject = m::mock('cIblockObject'); } @@ -344,16 +345,30 @@ public function testCount() public function testToArray() { - $element = new TestElement(1, ['ID' => 1, 'NAME' => 'John Doe']); + $element = new TestElement(1, ['ID' => 1, 'NAME' => 'John Doe', 'PROPERTY_LANG_ACCESSOR_ONE_RU_VALUE' => 'la_ru']); - $this->assertSame(['ID' => 1, 'NAME' => 'John Doe', 'ACCESSOR_THREE' => []], $element->toArray()); + $expected = [ + 'ID' => 1, + 'NAME' => 'John Doe', + 'ACCESSOR_THREE' => [], + 'PROPERTY_LANG_ACCESSOR_ONE_RU_VALUE' => 'la_ru', + 'PROPERTY_LANG_ACCESSOR_ONE' => 'la_ru' + ]; + $this->assertEquals($expected, $element->toArray()); } public function testToJson() { - $element = new TestElement(1, ['ID' => 1, 'NAME' => 'John Doe']); + $element = new TestElement(1, ['ID' => 1, 'NAME' => 'John Doe', 'PROPERTY_LANG_ACCESSOR_ONE_RU_VALUE' => 'la_ru',]); - $this->assertSame(json_encode(['ID' => 1, 'NAME' => 'John Doe', 'ACCESSOR_THREE' => []]), $element->toJson()); + $expected = [ + 'ID' => 1, + 'NAME' => 'John Doe', + 'PROPERTY_LANG_ACCESSOR_ONE_RU_VALUE' => 'la_ru', + 'ACCESSOR_THREE' => [], + 'PROPERTY_LANG_ACCESSOR_ONE' => 'la_ru' + ]; + $this->assertEquals(json_encode($expected), $element->toJson()); } public function testFill() @@ -401,7 +416,7 @@ public function testArrayAccess() public function testAccessors() { $element = new TestElement(1); - $element->fill(['ID' => 2, 'NAME' => 'John', 'ACCESSOR_ONE' => 'foo']); + $element->fill(['ID' => 2, 'NAME' => 'John', 'ACCESSOR_ONE' => 'foo', 'PROPERTY_LANG_ACCESSOR_ONE_RU_VALUE' => 'la_ru']); $this->assertSame('!foo!', $element['ACCESSOR_ONE']); $this->assertTrue(isset($element['ACCESSOR_ONE'])); @@ -412,6 +427,8 @@ public function testAccessors() $this->assertSame([], $element['ACCESSOR_THREE']); $this->assertTrue(isset($element['ACCESSOR_THREE'])); $this->assertFalse(!empty($element['ACCESSOR_THREE'])); + $this->assertTrue(isset($element['PROPERTY_LANG_ACCESSOR_ONE'])); + $this->assertSame('la_ru', $element['PROPERTY_LANG_ACCESSOR_ONE']); } public function testItPlaysNiceWithOtherBitrixModels() diff --git a/tests/ElementQueryTest.php b/tests/ElementQueryTest.php index 57e0a63..9f74cd9 100644 --- a/tests/ElementQueryTest.php +++ b/tests/ElementQueryTest.php @@ -77,8 +77,8 @@ public function testGetListWithKeyBy() $items = $query->filter(['ACTIVE' => 'N'])->select('ID', 'NAME')->getList(); $expected = [ - 1 => ['ID' => 1, 'NAME' => 'foo', 'ACCESSOR_THREE' => []], - 2 => ['ID' => 2, 'NAME' => 'bar', 'ACCESSOR_THREE' => []], + 1 => ['ID' => 1, 'NAME' => 'foo', 'ACCESSOR_THREE' => [], 'PROPERTY_LANG_ACCESSOR_ONE' => null], + 2 => ['ID' => 2, 'NAME' => 'bar', 'ACCESSOR_THREE' => [], 'PROPERTY_LANG_ACCESSOR_ONE' => null], ]; $this->assertSame($expected, $items->toArray()); diff --git a/tests/Stubs/TestElement.php b/tests/Stubs/TestElement.php index f375bae..d06036a 100644 --- a/tests/Stubs/TestElement.php +++ b/tests/Stubs/TestElement.php @@ -14,7 +14,9 @@ */ class TestElement extends ElementModel { - protected $appends = ['ACCESSOR_THREE']; + protected $appends = ['ACCESSOR_THREE', 'PROPERTY_LANG_ACCESSOR_ONE']; + + protected $languageAccessors = ['PROPERTY_LANG_ACCESSOR_ONE']; const IBLOCK_ID = 1;