├── src ├── IndexInterface.php ├── Exception.php ├── Event.php └── ElasticsearchIndexer.php ├── .github └── workflows │ └── tests.yml ├── LICENSE ├── composer.json └── README.md /src/IndexInterface.php: -------------------------------------------------------------------------------- 1 | indexAllModels = $indexAllModels; 17 | } 18 | 19 | 20 | 21 | public function afterSave(\Phalcon\Events\Event $event, ModelInterface $model, $data): void 22 | { 23 | if ($this->canModelBeIndexed($model)) { 24 | $this->elasticsearchIndexer->index($model); 25 | } 26 | } 27 | 28 | public function beforeDelete(\Phalcon\Events\Event $event, ModelInterface $model, $data): void 29 | { 30 | if ($this->canModelBeIndexed($model)) { 31 | $this->elasticsearchIndexer->delete($model); 32 | } 33 | } 34 | 35 | 36 | 37 | protected function canModelBeIndexed(ModelInterface $model): bool 38 | { 39 | return ($this->indexAllModels || ($model instanceof IndexInterface)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2025 Sid Roberts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sidroberts/phalcon-elasticsearchindexer", 3 | "description": "Elasticsearch indexer component for Phalcon.", 4 | "type": "library", 5 | "license": "MIT", 6 | 7 | "authors": [ 8 | { 9 | "name": "Sid Roberts", 10 | "email": "sid@sidroberts.co.uk", 11 | "homepage": "https://sidroberts.co.uk", 12 | "role": "Developer" 13 | } 14 | ], 15 | 16 | "support": { 17 | "source": "https://github.com/SidRoberts/phalcon-elasticsearchindexer", 18 | "issues": "https://github.com/SidRoberts/phalcon-elasticsearchindexer/issues", 19 | "wiki": "https://github.com/SidRoberts/phalcon-elasticsearchindexer/wiki" 20 | }, 21 | 22 | 23 | 24 | "require": { 25 | "php": "~8.0", 26 | 27 | "ext-phalcon": "~5.0", 28 | 29 | "elasticsearch/elasticsearch": "~6.0" 30 | }, 31 | 32 | "require-dev": { 33 | "codeception/codeception": "~5.0", 34 | "codeception/module-asserts": "~3.0", 35 | 36 | "squizlabs/php_codesniffer": "~3.4" 37 | }, 38 | 39 | 40 | 41 | "autoload": { 42 | "psr-4": { 43 | "Sid\\Phalcon\\ElasticsearchIndexer\\": "src/" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Sid\Phalcon\ElasticsearchIndexer` 2 | 3 | Elasticsearch indexer component for Phalcon. 4 | 5 | 6 | 7 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/SidRoberts/phalcon-elasticsearchindexer/tests.yml?style=for-the-badge)](https://github.com/SidRoberts/phalcon-elasticsearchindexer/actions) 8 | [![GitHub release](https://img.shields.io/github/release/SidRoberts/phalcon-elasticsearchindexer.svg?style=for-the-badge)]() 9 | 10 | [![GitHub issues](https://img.shields.io/github/issues-raw/SidRoberts/phalcon-elasticsearchindexer.svg?style=for-the-badge)](https://github.com/SidRoberts/phalcon-elasticsearchindexer/issues) 11 | [![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/SidRoberts/phalcon-elasticsearchindexer.svg?style=for-the-badge)](https://github.com/SidRoberts/phalcon-elasticsearchindexer/pulls) 12 | 13 | 14 | 15 | ## Version Requirements 16 | 17 | [![](https://img.shields.io/badge/Phalcon-%3E%3D%205.0.0-76C39B?style=for-the-badge)]() 18 | 19 | [![](https://img.shields.io/badge/PHP-%3E%3D%208.0.0-777BB3?style=for-the-badge)]() 20 | 21 | 22 | 23 | ## Installing 24 | 25 | Install using Composer: 26 | 27 | ```json 28 | { 29 | "require": { 30 | "sidroberts/phalcon-elasticsearchindexer": "dev-main" 31 | } 32 | } 33 | ``` 34 | 35 | 36 | 37 | ## Documentation 38 | 39 | See the [Wiki](https://github.com/SidRoberts/phalcon-elasticsearchindexer/wiki). 40 | 41 | 42 | 43 | ## License 44 | 45 | Licensed under the MIT License. 46 | © [Sid Roberts](https://github.com/SidRoberts) 47 | -------------------------------------------------------------------------------- /src/ElasticsearchIndexer.php: -------------------------------------------------------------------------------- 1 | getDI(); 25 | 26 | if (!($di instanceof DiInterface)) { 27 | throw new Exception( 28 | "A dependency injection object is required to access internal services" 29 | ); 30 | } 31 | 32 | $this->index = $index; 33 | } 34 | 35 | 36 | 37 | public function getEventsManager(): ?EventsManagerInterface 38 | { 39 | return $this->eventsManager; 40 | } 41 | 42 | public function setEventsManager(EventsManagerInterface $eventsManager): void 43 | { 44 | $this->eventsManager = $eventsManager; 45 | } 46 | 47 | 48 | 49 | /** 50 | * @throws Exception 51 | */ 52 | public function index(ModelInterface $model): array 53 | { 54 | $eventsManager = $this->getEventsManager(); 55 | 56 | if ($eventsManager instanceof EventsManagerInterface) { 57 | $eventsManager->fire("search:beforeIndex", $this); 58 | } 59 | 60 | $response = $this->elasticsearch->index( 61 | [ 62 | "index" => $this->index, 63 | "type" => $model->getSource(), 64 | "id" => $this->getPrimaryKeyValue($model), 65 | "body" => $model->toArray(), 66 | ] 67 | ); 68 | 69 | if ($eventsManager instanceof EventsManagerInterface) { 70 | $eventsManager->fire("search:afterIndex", $this); 71 | } 72 | 73 | return $response; 74 | } 75 | 76 | 77 | 78 | /** 79 | * @throws Exception 80 | */ 81 | public function delete(ModelInterface $model): array 82 | { 83 | $eventsManager = $this->getEventsManager(); 84 | 85 | if ($eventsManager instanceof EventsManagerInterface) { 86 | $eventsManager->fire("search:beforeDelete", $this); 87 | } 88 | 89 | $response = $this->elasticsearch->delete( 90 | [ 91 | "index" => $this->index, 92 | "type" => $model->getSource(), 93 | "id" => $this->getPrimaryKeyValue($model), 94 | ] 95 | ); 96 | 97 | if ($eventsManager instanceof EventsManagerInterface) { 98 | $eventsManager->fire("search:afterDelete", $this); 99 | } 100 | 101 | return $response; 102 | } 103 | 104 | 105 | 106 | /** 107 | * @throws Exception 108 | */ 109 | protected function getPrimaryKeyValue(ModelInterface $model): mixed 110 | { 111 | $primaryKeyAttributes = $this->modelsMetadata->getPrimaryKeyAttributes($model); 112 | 113 | if (count($primaryKeyAttributes) !== 1) { 114 | throw new Exception( 115 | "Model does not have a single Primary Key field." 116 | ); 117 | } 118 | 119 | $primaryKeyAttribute = $primaryKeyAttributes[0]; 120 | 121 | return $model->readAttribute($primaryKeyAttribute); 122 | } 123 | } 124 | --------------------------------------------------------------------------------