{"id":13877764,"url":"https://github.com/yii2tech/embedded","last_synced_at":"2025-10-01T02:31:16.097Z","repository":{"id":57087037,"uuid":"39000180","full_name":"yii2tech/embedded","owner":"yii2tech","description":"Support embedded models usage for complex ActiveRecord like MongoDB or ElasticSearch","archived":true,"fork":false,"pushed_at":"2020-02-19T11:37:06.000Z","size":40,"stargazers_count":81,"open_issues_count":0,"forks_count":15,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-09-22T22:01:57.493Z","etag":null,"topics":["activerecord","complex-attributes","elasticsearch","embed","embedded","embedded-models","embedded-objects","mongodb","yii","yii2","yii2-extension"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yii2tech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["klimov-paul"],"patreon":"klimov_paul"}},"created_at":"2015-07-13T08:25:20.000Z","updated_at":"2023-10-07T00:37:12.000Z","dependencies_parsed_at":"2022-08-24T22:50:48.897Z","dependency_job_id":null,"html_url":"https://github.com/yii2tech/embedded","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fembedded","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fembedded/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fembedded/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fembedded/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yii2tech","download_url":"https://codeload.github.com/yii2tech/embedded/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219875269,"owners_count":16554660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","complex-attributes","elasticsearch","embed","embedded","embedded-models","embedded-objects","mongodb","yii","yii2","yii2-extension"],"created_at":"2024-08-06T08:01:22.916Z","updated_at":"2025-10-01T02:31:15.762Z","avatar_url":"https://github.com/yii2tech.png","language":"PHP","readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/yii2tech\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://avatars2.githubusercontent.com/u/12951949\" height=\"100px\"\u003e\n    \u003c/a\u003e\n    \u003ch1 align=\"center\"\u003eEmbedded (Nested) Models Extension for Yii 2\u003c/h1\u003e\n    \u003cbr\u003e\n\u003c/p\u003e\n\nThis extension provides support for embedded (nested) models usage in Yii2.\nIn particular it allows working with sub-documents in [MongoDB](https://github.com/yiisoft/yii2-mongodb) and [ElasticSearch](https://github.com/yiisoft/yii2-elasticsearch)\nas well as processing complex JSON attributes at relational databases.\n\nFor license information check the [LICENSE](LICENSE.md)-file.\n\n[![Latest Stable Version](https://poser.pugx.org/yii2tech/embedded/v/stable.png)](https://packagist.org/packages/yii2tech/embedded)\n[![Total Downloads](https://poser.pugx.org/yii2tech/embedded/downloads.png)](https://packagist.org/packages/yii2tech/embedded)\n[![Build Status](https://travis-ci.org/yii2tech/embedded.svg?branch=master)](https://travis-ci.org/yii2tech/embedded)\n\n\nInstallation\n------------\n\nThe preferred way to install this extension is through [composer](http://getcomposer.org/download/).\n\nEither run\n\n```\nphp composer.phar require --prefer-dist yii2tech/embedded\n```\n\nor add\n\n```json\n\"yii2tech/embedded\": \"*\"\n```\n\nto the require section of your composer.json.\n\n\nUsage\n-----\n\nThis extension grants the ability to work with complex model attributes, represented as arrays, as nested models,\nrepresented as objects.\nTo use this feature the target class should implement [[\\yii2tech\\embedded\\ContainerInterface]] interface.\nThis can be easily achieved using [[\\yii2tech\\embedded\\ContainerTrait]].\n\nFor each embedded entity a mapping declaration should be provided.\nIn order to do so you need to declare method, which name is prefixed with 'embedded', which\nshould return the [[Mapping]] instance. You may use [[hasEmbedded()]] and [[hasEmbeddedList()]] for this.\n\nPer each of source field or property a new virtual property will be declared, which name will be composed\nby removing 'embedded' prefix from the declaration method name.\n\n\u003e Note: watch for the naming collisions: if you have a source property named 'profile' the mapping declaration\n  for it should have different name, like 'profileModel'.\n\n\nExample:\n\n```php\nuse yii\\base\\Model;\nuse yii2tech\\embedded\\ContainerInterface;\nuse yii2tech\\embedded\\ContainerTrait;\n\nclass User extends Model implements ContainerInterface\n{\n    use ContainerTrait;\n\n    public $profileData = [];\n    public $commentsData = [];\n\n    public function embedProfile()\n    {\n        return $this-\u003emapEmbedded('profileData', Profile::className());\n    }\n\n    public function embedComments()\n    {\n        return $this-\u003emapEmbeddedList('commentsData', Comment::className());\n    }\n}\n\n$user = new User();\n$user-\u003eprofile-\u003efirstName = 'John';\n$user-\u003eprofile-\u003elastName = 'Doe';\n\n$comment = new Comment();\n$user-\u003ecomments[] = $comment;\n```\n\nEach embedded mapping may have additional options specified. Please refer to [[\\yii2tech\\embedded\\Mapping]] for more details.\n\n\n## Processing embedded objects \u003cspan id=\"processing-embedded-objects\"\u003e\u003c/span\u003e\n\nEmbedded feature is similar to regular ActiveRecord relation feature. Their declaration and processing are similar\nand have similar specifics and limitations.\nAll embedded objects are lazy loaded. This means they will not be created until first demand. This saves memory\nbut may produce unexpected results at some point.\nBy default, once embedded object is instantiated its source attribute will be unset in order to save memory usage.\nYou can control this behavior via [[\\yii2tech\\embedded\\Mapping::$unsetSource]].\n\nEmbedded objects allow simplification of nested data processing, but usually they know nothing about their source\ndata meaning and global processing. For example: nested object is not aware if its source data comes from database\nand it does not know how this data should be saved. Such functionality usually is handled by container object.\nThus at some point you will need to convert data from embedded objects back to its raw format, which allows its\nnative processing like saving. This can be done using method `refreshFromEmbedded()`:\n\n```php\nuse yii\\base\\Model;\nuse yii2tech\\embedded\\ContainerInterface;\nuse yii2tech\\embedded\\ContainerTrait;\n\nclass User extends Model implements ContainerInterface\n{\n    use ContainerTrait;\n\n    public $profileData = [\n        'firstName' =\u003e 'Unknown',\n        'lastName' =\u003e 'Unknown',\n    ];\n\n    public function embedProfile()\n    {\n        return $this-\u003emapEmbedded('profileData', Profile::className());\n    }\n}\n\n$user = new User();\nvar_dump($user-\u003eprofileData); // outputs array: ['firstName' =\u003e 'Unknown', 'lastName' =\u003e 'Unknown']\n\n$user-\u003eprofile-\u003efirstName = 'John';\n$user-\u003eprofile-\u003elastName = 'Doe';\n\nvar_dump($user-\u003eprofileData); // outputs empty array\n\n$user-\u003erefreshFromEmbedded();\nvar_dump($user-\u003eprofileData); // outputs array: ['firstName' =\u003e 'John', 'lastName' =\u003e 'Doe']\n```\n\nWhile embedding list of objects (using [[\\yii2tech\\embedded\\ContainerTrait::mapEmbeddedList()]]) the produced\nvirtual field will be not an array, but an object, which satisfies [[\\ArrayAccess]] interface. Thus all manipulations\nwith such property (even if it may look like using array) will affect container object.\nFor example:\n\n```php\nuse yii\\base\\Model;\nuse yii2tech\\embedded\\ContainerInterface;\nuse yii2tech\\embedded\\ContainerTrait;\n\nclass User extends Model implements ContainerInterface\n{\n    use ContainerTrait;\n\n    public $commentsData = [];\n\n    public function embedComments()\n    {\n        return $this-\u003emapEmbeddedList('commentsData', Comment::className());\n    }\n}\n\n$user = new User();\n// ...\n\n$comments = $user-\u003ecomments; // not a copy of array - copy of object reference!\nforeach ($comments as $key =\u003e $comment) {\n    if (...) {\n        unset($comments[$key]); // unsets `$user-\u003ecomments[$key]`!\n    }\n}\n\n$comments = clone $user-\u003ecomments; // creates a copy of list, but not a copy of contained objects!\n$comments[0]-\u003etitle = 'new value'; // actually sets `$user-\u003ecomments[0]-\u003etitle`!\n```\n\n\n## Validating embedded models \u003cspan id=\"validating-embedded-models\"\u003e\u003c/span\u003e\n\nEach embedded model should declare its own validation rules and, in general, should be validated separately.\nHowever, you may simplify complex model validation using [[\\yii2tech\\embedded\\Validator]].\nFor example:\n\n```php\nuse yii\\base\\Model;\nuse yii2tech\\embedded\\ContainerInterface;\nuse yii2tech\\embedded\\ContainerTrait;\n\nclass User extends Model implements ContainerInterface\n{\n    use ContainerTrait;\n\n    public $contactData;\n\n    public function embedContact()\n    {\n        return $this-\u003emapEmbedded('contactData', Contact::className());\n    }\n\n    public function rules()\n    {\n        return [\n            ['contact', 'yii2tech\\embedded\\Validator'],\n            // ...\n        ]\n    }\n}\n\nclass Contact extends Model\n{\n    public $email;\n\n    public function rules()\n    {\n        return [\n            ['email', 'required'],\n            ['email', 'email'],\n        ]\n    }\n}\n\n$user = new User();\nif ($user-\u003eload(Yii::$app-\u003erequest-\u003epost()) \u0026\u0026 $user-\u003econtact-\u003eload(Yii::$app-\u003erequest-\u003epost())) {\n    if ($user-\u003evalidate()) { // automatically validates 'contact' as well\n        // ...\n    }\n}\n```\n\n\u003e Note: pay attention that [[\\yii2tech\\embedded\\Validator]] must be set for the embedded model name - not for its\n  source attribute. Do not mix them up!\n\nYou can enable [[\\yii2tech\\embedded\\Validator::$initializedOnly]], allowing to skip validation for the embedded model, if\nit has not been initialized, e.g. requested at least once. This will save the performance in case source model can be used\nin different scenarios, some of which may not require embedded model manipulations. However, in this case embedded source\nattribute value will not be validated. You should ensure it validated in other way or it is 'unsafe' for population via\n[[\\yii\\base\\Model::load()]] method.\n\n\n## Saving embedded models \u003cspan id=\"saving-embedded-models\"\u003e\u003c/span\u003e\n\nKeep in mind that embedded models are stored separately from the source model attributes. You will need to use\n[[\\yii2tech\\embedded\\ContainerInterface::refreshFromEmbedded()]] method in order to populate source model\nattributes with the data from embedded models.\n\nAlso note, that attempt to get 'dirty' value for embedded source attribute will also fail until you use `refreshFromEmbedded()`\neven, if embedded model has changed:\n\n```php\n$user = User::findOne(1); // declares embedded model 'contactModel' from attribute 'contactData'\n\nif ($user-\u003econtactModel-\u003eload(Yii::$app-\u003erequest-\u003epost())) {\n    var_dump($user-\u003eisAttributeChanged('contactData')); // outputs `false`\n\n    $user-\u003erefreshFromEmbedded();\n    var_dump($user-\u003eisAttributeChanged('contactData')); // outputs `true`\n}\n```\n\nIn case you are applying 'embedded' functionality to an ActiveRecord class, the best place for the data synchronization\nis [[\\yii\\db\\BaseActiveRecord::beforeSave()]] method. For example, application of this extension to the [[\\yii\\mongodb\\ActiveRecord]]\nclass may look like following:\n\n```php\nuse yii2tech\\embedded\\ContainerInterface;\nuse yii2tech\\embedded\\ContainerTrait;\n\nclass ActiveRecord extends \\yii\\mongodb\\ActiveRecord implements ContainerInterface\n{\n    use ContainerTrait;\n\n    public function beforeSave($insert)\n    {\n        if (!parent::beforeSave($insert)) {\n            return false;\n        }\n        $this-\u003erefreshFromEmbedded(); // populate this model attributes from embedded models' ones, ensuring they are marked as 'changed' before saving\n        return true;\n    }\n}\n```\n\n\n## Predefined model classes \u003cspan id=\"predefined-model-classes\"\u003e\u003c/span\u003e\n\nThis extension is generic and may be applied to any model with complex attributes. However, to simplify integration with\ncommon solutions several base classes are provided by this extension:\n - [[\\yii2tech\\embedded\\mongodb\\ActiveRecord]] - MongoDB ActiveRecord with embedded feature built-in\n - [[\\yii2tech\\embedded\\mongodb\\ActiveRecordFile]] - MongoDB GridFS ActiveRecord with embedded feature built-in\n - [[\\yii2tech\\embedded\\elasticsearch\\ActiveRecord]] - ElasticSearch ActiveRecord with embedded feature built-in\n\nProvided ActiveRecord classes already implement [[\\yii2tech\\embedded\\ContainerInterface]] and invoke `refreshFromEmbedded()`\non `beforeSave()` stage.\nFor example, if you are using MongoDB and wish to work with sub-documents, you may simply switch extending from\nregular [[\\yii\\mongodb\\ActiveRecord]] to [[\\yii2tech\\embedded\\mongodb\\ActiveRecord]]:\n\n```php\nclass User extends \\yii2tech\\embedded\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'customer';\n    }\n\n    public function attributes()\n    {\n        return ['_id', 'name', 'email', 'addressData', 'status'];\n    }\n\n    public function embedAddress()\n    {\n        return $this-\u003emapEmbedded('addressData', UserAddress::className());\n    }\n}\n```\n","funding_links":["https://github.com/sponsors/klimov-paul","https://patreon.com/klimov_paul"],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyii2tech%2Fembedded","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyii2tech%2Fembedded","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyii2tech%2Fembedded/lists"}