├── .codacy.yml ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── build-ci.yml ├── .gitignore ├── .scrutinizer.yml ├── .styleci.yml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config └── laravel-mongo-auto-sync.php ├── docker-compose.yml ├── phpcs.xml ├── phpstan.neon ├── phpunit.xml ├── src ├── Console │ ├── DropCollection.php │ └── GenerateModelDocumentation.php ├── Extensions │ └── MongoCollection.php ├── Http │ └── Models │ │ ├── DefaultGeo.php │ │ ├── DefaultMini.php │ │ ├── MDModel.php │ │ └── MSModel.php ├── MongoAutoSyncServiceProvider.php ├── Traits │ ├── Helper.php │ ├── MainMongoTrait.php │ ├── ModelAdditionalMethod.php │ ├── PlainMongoTrait.php │ └── RelationshipMongoTrait.php └── syncUtils.php └── tests ├── BrowserKitTestCase.php ├── CreatesApplication.php ├── DropCollectionTest.php ├── GenerateModelDocumentationTest.php ├── HelperTest.php ├── MDModelTest.php ├── MainMongoTraitTest.php ├── ModelAdditionalMethodTest.php ├── Models ├── Article.php ├── Category.php ├── Item.php ├── MiniArticle.php ├── MiniCategory.php ├── MiniItem.php ├── MiniNavigation.php ├── MiniPermission.php ├── MiniRole.php ├── MiniSubItem.php ├── MiniUser.php ├── Navigation.php ├── Permission.php ├── Role.php ├── SubItem.php └── User.php ├── MongoCollectionTest.php ├── MongoSyncTraitTest.php ├── SimpleTestCase.php ├── StoreWIthSyncTest.php ├── SyncTestCase.php ├── SyncUtilsTest.php ├── TestCase.php ├── UpdateWithSyncTest.php └── config ├── database.php └── queue.php /.codacy.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - '.github/**' 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: offline-agency 2 | -------------------------------------------------------------------------------- /.github/workflows/build-ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | tags: 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | php: ['7.4', '8.0', '8.1'] 15 | os: ['ubuntu-latest'] 16 | mongodb: ['3.6', '4.0', '4.2', '5.0', '6.0', '7.0'] 17 | services: 18 | mongo: 19 | image: mongo:${{ matrix.mongodb }} 20 | ports: 21 | - 27017:27017 22 | 23 | name: PHP v${{ matrix.php }} with Mongo v${{ matrix.mongodb }} 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: "Installing php" 27 | uses: shivammathur/setup-php@v2 28 | with: 29 | php-version: ${{ matrix.php }} 30 | extensions: curl,mbstring,xdebug 31 | coverage: xdebug 32 | tools: composer 33 | - name: Show PHP version 34 | run: php -v && composer -V 35 | - name: Show Dockerfile version 36 | run: if [[ "$DEBUG" == "true" ]]; then docker version && env; fi 37 | env: 38 | DEBUG: ${{secrets.DEBUG}} 39 | - name: Download Composer cache dependencies from cache 40 | id: composer-cache 41 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 42 | - name: Cache Composer dependencies 43 | uses: actions/cache@v1 44 | with: 45 | path: ${{ steps.composer-cache.outputs.dir }} 46 | key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }} 47 | restore-keys: ${{ matrix.os }}-composer- 48 | - name: Install dependencies 49 | run: | 50 | composer install --no-interaction 51 | - name: Run tests 52 | run: | 53 | ./vendor/bin/phpunit --coverage-clover coverage.xml 54 | env: 55 | MONGO_HOST: 0.0.0.0 56 | - uses: codecov/codecov-action@v1 57 | with: 58 | token: ${{ secrets.CODECOV_TOKEN }} 59 | fail_ci_if_error: false 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | vendor/ 4 | storage/ 5 | bootstrap/ 6 | .idea 7 | .phpunit.result.cache 8 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | environment: 3 | php: 4 | version: 7.4 5 | memcached: true 6 | mongodb: true 7 | 8 | nodes: 9 | my-node: 10 | services: 11 | # For available tags, see https://hub.docker.com/_/mongo/ 12 | mongo: 4 13 | analysis: 14 | project_setup: 15 | override: true 16 | tests: 17 | override: 18 | - php-scrutinizer-run 19 | - command: 'vendor/bin/phpunit --dump-xdebug-filter xdebug-filter.php' 20 | checks: 21 | 22 | php: 23 | remove_extra_empty_lines: true 24 | remove_php_closing_tag: true 25 | remove_trailing_whitespace: true 26 | 27 | fix_use_statements: 28 | remove_unused: true 29 | preserve_multiple: false 30 | preserve_blanklines: true 31 | order_alphabetically: true 32 | 33 | fix_php_opening_tag: true 34 | fix_linefeed: true 35 | fix_line_ending: true 36 | fix_identation_4spaces: true 37 | fix_doc_comments: true 38 | 39 | coding_style: 40 | php: 41 | spaces: 42 | before_parentheses: 43 | closure_definition: true 44 | other: 45 | after_type_cast: false 46 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Awesome! How do I contribute? 2 | Contributions are welcome, and are accepted via pull requests. Please review these guidelines before submitting any pull requests. 3 | 4 | ## Guidelines 5 | - Please follow the [PSR-2 Coding Standard](https://www.php-fig.org/psr/psr-2/) and [PHP-FIG Naming Conventions](https://www.php-fig.org/bylaws/psr-naming-conventions/). 6 | 7 | - Ensure that the current tests pass, and if you've added something new, add the tests where relevant. 8 | 9 | - Remember that we follow [SemVer](https://semver.org/). If you are changing the behaviour, or the public api, you may need to update the docs. 10 | 11 | - Send a coherent commit history, making sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) them before submitting. 12 | 13 | - You may also need to [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) to avoid merge conflicts. 14 | 15 | 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=7.2 2 | ARG COMPOSER_VERSION=1.8 3 | 4 | FROM composer:${COMPOSER_VERSION} 5 | FROM php:${PHP_VERSION}-cli 6 | 7 | RUN apt-get update && \ 8 | apt-get install -y autoconf pkg-config libssl-dev git libzip-dev zlib1g-dev && \ 9 | pecl install mongodb && docker-php-ext-enable mongodb && \ 10 | pecl install xdebug && docker-php-ext-enable xdebug && \ 11 | docker-php-ext-install -j$(nproc) pdo_mysql zip 12 | 13 | COPY --from=composer /usr/bin/composer /usr/local/bin/composer 14 | 15 | WORKDIR /code 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Offline Agency 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel MongoDB Relationships 2 | [![Latest Stable Version](https://poser.pugx.org/offline-agency/laravel-mongo-auto-sync/v/stable)](https://packagist.org/packages/offline-agency/laravel-mongo-auto-sync) 3 | [![Total Downloads](https://img.shields.io/packagist/dt/offline-agency/laravel-mongo-auto-sync.svg?style=flat-square)](https://packagist.org/packages/offline-agency/laravel-mongo-auto-sync) 4 | [![Build Status](https://github.com/offline-agency/laravel-mongo-auto-sync/actions/workflows/build-ci.yml/badge.svg)](https://github.com/offline-agency/laravel-mongo-auto-sync/actions/workflows/build-ci.yml) 5 | [![MIT Licensed](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 6 | [![Quality Score](https://img.shields.io/scrutinizer/g/offline-agency/laravel-mongo-auto-sync.svg?style=flat-square)](https://scrutinizer-ci.com/g/offline-agency/laravel-mongo-auto-sync) 7 | [![StyleCI](https://github.styleci.io/repos/167277388/shield)](https://styleci.io/repos/167277388) 8 | [![codecov](https://codecov.io/gh/offline-agency/laravel-mongo-auto-sync/branch/master/graph/badge.svg?token=0BHADJQYAW)](https://codecov.io/gh/offline-agency/laravel-mongo-auto-sync) 9 | 10 | This package provides a better support for [MongoDB](https://www.mongodb.com) relationships in [Laravel](https://laravel.com/) Projects. 11 | At low level all CRUD operations has been handled by [jenssegers/laravel-mongodb](https://github.com/jenssegers/laravel-mongodb) 12 | 13 | ## Installation 14 | 15 | ```bash 16 | composer require offline-agency/laravel-mongo-auto-sync 17 | ``` 18 | 19 | ### Prerequisites 20 | Make sure you have the MongoDB PHP driver installed. You can find installation instructions at [http://php.net/manual/en/mongodb.installation.php](http://php.net/manual/en/mongodb.installation.php) 21 | 22 | ### Package version Compatibility 23 | 24 | | This package | Laravel | Laravel MongoDB | 25 | |--------------|---------|-----------------| 26 | | 1.x | 5.8.x | 3.5.x | 27 | | 1.x | 6.x | 3.6.x | 28 | | 2.x | 5.8.x | 3.5.x | 29 | | 2.x | 6.x | 3.6.x | 30 | | 2.x | 7.x | 3.7.x | 31 | | 2.x | 8.x | 3.8.x | 32 | | 2.x | 9.x | 3.9.x | 33 | | 3.x | 5.8.x | 3.5.x | 34 | | 3.x | 6.x | 3.6.x | 35 | | 3.x | 7.x | 3.7.x | 36 | | 3.x | 8.x | 3.8.x | 37 | | 3.x | 9.x | 3.9.x | 38 | 39 | ### PHP Version Compatibility 40 | - Version 1: PHP 7.1, 7.2, 7.3 41 | - Version 2: PHP 7.4 42 | - Version 3: PHP 7.4-8.1 43 | 44 | ## Features 45 | - Sync changes between collection with relationships after CRUD operations 46 | - EmbedsOne & EmbedsMany 47 | 48 | #### Example without our package 49 | 50 | ``` php 51 | //create a new Article with title "Game of Thrones" with Category "TV Series" 52 | //assign data to $article 53 | $article->save(); 54 | /* 55 | Article::class { 56 | 'title' => 'Game of Thrones', 57 | 'category' => Category::class { 58 | 'name' => 'TV Series' 59 | } 60 | } 61 | */ 62 | 63 | //Retrieve 'TV Series' category 64 | $category = Category::where('name', 'TV Series')->first(); 65 | /* 66 | Category::class { 67 | 'name' => 'Game of Thrones', 68 | 'articles' => null 69 | } 70 | */ 71 | ``` 72 | 73 | The sub document article has not been updated with the new article. So you will need some extra code to write in order to see the new article it in the category page. The number of sync depends on the number of the relationships and on the number of the entry in every single EmbedsMany relationships. 74 | 75 | Total updates = ∑ (entry in all EmbedsMany relationships) + ∑ (EmbedsOne relationships) 76 | 77 | As you can see the lines of extra code can rapidly increase, and you will write many redundant code. 78 | 79 | #### Example with our package 80 | 81 | ``` php 82 | //create a new Article with title "Game of Thrones" with Category "TV Series" 83 | $article->storeWithSync($request); 84 | /* 85 | Article::class { 86 | 'title' => 'Game of Thrones', 87 | 'category' => Category::class { 88 | 'name' => 'TV Series' 89 | } 90 | } 91 | */ 92 | //Retrieve 'TV Series' category 93 | $category = Category::where('name', 'TV Series')->first(); 94 | /* 95 | Category::class { 96 | 'name' => 'Game of Thrones', 97 | 'articles' => Article::class { 98 | 'title' => 'Game of Thrones' 99 | } 100 | } 101 | */ 102 | ``` 103 | The sub document article has been updated with the new article, with no need of extra code :tada: 104 | 105 | You can see the new article on the category page because the package synchronizes the information for you by reading the Model Setup. 106 | 107 | **These example can be applied for all write operations on the database.** 108 | - Referenced sub documents [TO DO] 109 | - Handle sub document as Model in order to exploit Laravel ORM support during write operation (without sync feature) [TO BE TEST] 110 | - Handle referenced sub document as Model in order to exploit Laravel ORM support during write operation (without sync feature) [TO DO] 111 | - Advance cast field support 112 | 113 | ## Use cases 114 | - Blog: see demo [here](https://github.com/offline-agency/laravel-mongodb-blog) 115 | - Ecommerce 116 | - API System for mobile application o for generated static site 117 | - Any projects that require fast read operations and (slow) write operations that can be run on background 118 | 119 | ## Documentation 120 | You can find the documentation [here](https://docs.offlineagency.com/laravel-mongo-auto-sync/) 121 | 122 | ## Testing 123 | 124 | Run this command inside your project's route 125 | ``` bash 126 | docker-compose up 127 | ``` 128 | 129 | Now run the tests with: 130 | ``` bash 131 | composer test 132 | ``` 133 | 134 | ## Roadmap :rocket: 135 | - Refactor target synchronization to Observer pattern, so all this operation can be run on background using [Laravel Queue System](https://laravel.com/docs/5.8/queues). This will also speed up all the operations in the collection that is primary involved in write operations. 136 | - Command Analyse Database: This command will analyse the database in order to find some relationship error. 137 | Ex: An article with a category associated that is not present on the Category's sub document. 138 | - Refactor **save()** method in order to handle CRUD operation on relationship also without sync. 139 | - Support for [referenced relationships](https://docs.mongodb.com/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/). 140 | - Better support for all field types. 141 | - DestroyWithSync() without delete sub documents on other collections. 142 | - Add more tests. 143 | - Nested relationships. 144 | - Benchmark MongoDB vs Mysql (write and read operation). 145 | - Fix typo errors. 146 | 147 | ## Contributing 148 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 149 | 150 | ## Security 151 | If you discover any security-related issues, please email support@offlineagency.com instead of using the issue tracker. 152 | 153 | 154 | 155 | ## Credits 156 | - [Giacomo Fabbian](https://github.com/Giacomo92) 157 | 158 | - [All Contributors](https://github.com/offline-agency/laravel-mongo-auto-sync/graphs/contributors) 159 | 160 | ## About us 161 | Offline Agency is a web design agency based in Padua, Italy. You'll find an overview of our projects [on our website](https://offlineagency.it/#home). 162 | 163 | ## License 164 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 165 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "offline-agency/laravel-mongo-auto-sync", 3 | "description": "This package provides a better support for MongoDB relationships in Laravel Projects.", 4 | "type": "library", 5 | "keywords": ["laravel","database","mongodb","eloquent", "relationships"], 6 | "require": { 7 | "php": ">=7.4", 8 | "ext-json": "*", 9 | "ext-mongodb": "*", 10 | "illuminate/support": "^5.0|^6.0|^7.0|^8.0", 11 | "illuminate/container": "^5.0|^6.0|^7.0|^8.0", 12 | "illuminate/database": "^5.0|^6.0|^7.0|^8.0", 13 | "illuminate/events": "^5.0|^6.0|^7.0|^8.0", 14 | "jenssegers/mongodb": "^3.6|^3.7|^3.8" 15 | }, 16 | "require-dev": { 17 | "orchestra/testbench": "^3.0|^4.0|^5.0|^6.0", 18 | "phpunit/phpcov": "^7.0|^8.0", 19 | "phpunit/phpunit": "^5.0|^6.0|^7.0|^8.0|^9.0" 20 | }, 21 | "license": "MIT", 22 | "authors": [ 23 | { 24 | "name": "Offline Agency", 25 | "email": "info@offlineagency.it" 26 | } 27 | ], 28 | "autoload": { 29 | "psr-4": { 30 | "OfflineAgency\\MongoAutoSync\\": "src/" 31 | }, 32 | "files": [ 33 | "src/syncUtils.php" 34 | ] 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Tests\\": "tests/" 39 | }, 40 | "files": [ 41 | "src/syncUtils.php" 42 | ] 43 | }, 44 | "scripts": { 45 | "test": "vendor/bin/phpunit" 46 | }, 47 | "config": { 48 | "preferred-install": "dist", 49 | "sort-packages": true, 50 | "optimize-autoloader": true 51 | }, 52 | "extra": { 53 | "laravel": { 54 | "providers": [ 55 | "OfflineAgency\\MongoAutoSync\\MongoAutoSyncServiceProvider" 56 | ] 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /config/laravel-mongo-auto-sync.php: -------------------------------------------------------------------------------- 1 | app_path().'/Models', 5 | 'model_namespace' => 'App\Models', 6 | 'other_models' => [ 7 | 'user' => [ 8 | 'model_path' => app_path(), 9 | 'model_namespace' => 'App', 10 | ], 11 | ], 12 | ]; 13 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | tests: 5 | container_name: tests 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | volumes: 10 | - .:/code 11 | working_dir: /code 12 | depends_on: 13 | - mongodb 14 | mongodb: 15 | container_name: mongodb_ 16 | image: mongo 17 | ports: 18 | - 27017:27017 19 | logging: 20 | driver: none 21 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Coding standard for Laravel package boilerplate. 4 | src 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 7 3 | paths: ['src/'] 4 | autoload_files: 5 | - vendor/autoload.php 6 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests 14 | 15 | 16 | 17 | 18 | ./src 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Console/DropCollection.php: -------------------------------------------------------------------------------- 1 | argument('collection_name'); 44 | 45 | $modelPath = $this->getModelPathByName($collection_name); 46 | 47 | $model = $this->getModel($modelPath); 48 | 49 | if (! is_null($model)) { 50 | $model = $model->all(); 51 | 52 | $count = $model->count(); 53 | $bar = $this->output->createProgressBar($count); 54 | 55 | if ($count > 0) { 56 | for ($i = 0; $i <= $count - 1; $i++) { 57 | $bar->advance(); 58 | $model[$i]->destroyWithSync(); 59 | $this->line($i + 1 .') Destroy item document with id #'.$model[$i]->getId()); 60 | } 61 | } else { 62 | $this->warn('No record found on collection '.strtolower($collection_name)); 63 | } 64 | } else { 65 | $this->error('Error Model not found \n'); 66 | } 67 | } 68 | 69 | /** 70 | * @param $collection_name 71 | * @return string 72 | * 73 | * @throws Exception 74 | */ 75 | public function getModelPathByName($collection_name) 76 | { 77 | $path = config('laravel-mongo-auto-sync.model_path'); 78 | 79 | return $this->checkOaModels($path, $collection_name); 80 | } 81 | 82 | /** 83 | * @param $path 84 | * @param $collection_name 85 | * @return string 86 | * 87 | * @throws Exception 88 | */ 89 | public function checkOaModels($path, $collection_name) 90 | { 91 | $out = ''; 92 | 93 | try { 94 | $results = scandir($path); 95 | } catch (Exception $e) { 96 | throw new Exception('Error directory '.config('laravel-mongo-auto-sync.model_path').' not found'); 97 | } 98 | 99 | foreach ($results as $result) { 100 | if ($result === '.' or $result === '..') { 101 | continue; 102 | } 103 | $filename = $path.'/'.$result; 104 | if (is_dir($filename)) { 105 | $out = $this->checkOaModels($filename, $collection_name); 106 | } elseif (strtolower(substr($result, 0, -4)) == strtolower($collection_name)) { 107 | return config('laravel-mongo-auto-sync.model_namespace').'\\'.substr($result, 0, -4); 108 | } 109 | } 110 | 111 | foreach (config('laravel-mongo-auto-sync.other_models') as $key => $values) { 112 | if (strtolower($collection_name) == $key) { 113 | return $values['model_namespace'].'\\'.Str::ucfirst($key); 114 | } 115 | } 116 | 117 | return $out; 118 | } 119 | 120 | /** 121 | * @param string $modelPath 122 | * @return MDModel 123 | * 124 | * @throws Exception 125 | */ 126 | private function getModel(string $modelPath) 127 | { 128 | if (class_exists($modelPath)) { 129 | return new $modelPath; 130 | } else { 131 | throw new Exception('Error '.$this->argument('collection_name').' Model not found'); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Console/GenerateModelDocumentation.php: -------------------------------------------------------------------------------- 1 | argument('collection_name'); 44 | 45 | $modelPath = $this->getModelPathByName($collection_name); 46 | 47 | $model = $this->getModel($modelPath); 48 | 49 | $items = $model->getItems(); 50 | $relations = $model->getMongoRelation(); 51 | 52 | $output = "\n\n\n\n/**\n*\n* Plain Fields\n* \n"; 53 | $output .= "* @property string \$id\n"; 54 | 55 | foreach ($items as $key => $item) { 56 | if (isML($item)) { 57 | $output .= '* @property array $'.$key."\n"; 58 | } else { 59 | $output .= '* @property string $'.$key."\n"; 60 | } 61 | } 62 | 63 | $output .= "*\n*\n*"; 64 | 65 | if (count($relations) > 0) { 66 | $output .= " Relationships\n*\n"; 67 | foreach ($relations as $key => $relation) { 68 | $modelTarget = str_replace("App\Models\\", '', $relation['model']); 69 | 70 | $output .= '* @property '.$modelTarget.' $'.$key."\n"; 71 | } 72 | $output .= "*\n**/ \n\n\n\n\n"; 73 | } 74 | 75 | $this->info($output); 76 | } 77 | 78 | /** 79 | * @param $collection_name 80 | * @return string 81 | * 82 | * @throws Exception 83 | */ 84 | public function getModelPathByName($collection_name) 85 | { 86 | $path = config('laravel-mongo-auto-sync.model_path'); 87 | 88 | return $this->checkOaModels($path, $collection_name); 89 | } 90 | 91 | /** 92 | * @param $path 93 | * @param $collection_name 94 | * @return string 95 | * 96 | * @throws Exception 97 | */ 98 | public function checkOaModels($path, $collection_name) 99 | { 100 | $out = ''; 101 | 102 | try { 103 | $results = scandir($path); 104 | } catch (Exception $e) { 105 | throw new Exception('Error directory '.config('laravel-mongo-auto-sync.model_path').' not found'); 106 | } 107 | 108 | foreach ($results as $result) { 109 | if ($result === '.' or $result === '..') { 110 | continue; 111 | } 112 | $filename = $path.'/'.$result; 113 | if (is_dir($filename)) { 114 | $out = $this->checkOaModels($filename, $collection_name); 115 | } elseif (strtolower(substr($result, 0, -4)) == strtolower($collection_name)) { 116 | return config('laravel-mongo-auto-sync.model_namespace').'\\'.substr($result, 0, -4); 117 | } 118 | } 119 | foreach (config('laravel-mongo-auto-sync.other_models') as $key => $values) { 120 | if (strtolower($collection_name) == $key) { 121 | return $values['model_namespace'].'\\'.Str::ucfirst($key); 122 | } 123 | } 124 | 125 | return $out; 126 | } 127 | 128 | /** 129 | * @param string $modelPath 130 | * @return MDModel 131 | * 132 | * @throws Exception 133 | */ 134 | private function getModel(string $modelPath) 135 | { 136 | if (class_exists($modelPath)) { 137 | return new $modelPath; 138 | } else { 139 | throw new Exception('Error '.$this->argument('collection_name').' Model not found'); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/Extensions/MongoCollection.php: -------------------------------------------------------------------------------- 1 | filter(function ($col) use ($category, $myslug, $cl) { 15 | if ($col->slug[$cl] === $myslug && $col->status === 'published' && $col->primarycategory->slug[$cl] === $category) { 16 | return true; 17 | } else { 18 | return false; 19 | } 20 | })->first(); 21 | if (! $out) {//Handler 404 Object Not Found 22 | $obj_name = get_class($this->first()); 23 | $message = __('error.'.$obj_name); 24 | abort(404, $message); 25 | } else { 26 | return $out; 27 | } 28 | } 29 | 30 | /** 31 | * @param null $myslug 32 | * @return mixed 33 | */ 34 | public function getBySlug($myslug = null) 35 | { 36 | $cl = cl(); 37 | $out = $this->filter(function ($col) use ($myslug, $cl) { 38 | if ($col->slug[$cl] === $myslug) { 39 | return true; 40 | } 41 | })->first(); 42 | if (! $out) {//Handler 404 Object Not Found 43 | $obj_name = get_class($this->first()); 44 | $message = __('error.'.$obj_name); 45 | abort(404, $message); 46 | } else { 47 | return $out; 48 | } 49 | } 50 | 51 | //Method to retrieve only not deleted item of a collection - Check on is_deleted custom property added on MDMODEL ovverriding init, delete 52 | 53 | /** 54 | * @return MongoCollection 55 | */ 56 | public function getNotDeleted() 57 | { 58 | return $this->filter(function ($col) { 59 | if ($col->is_deleted) { 60 | return false; 61 | } else { 62 | return true; 63 | } 64 | }); 65 | } 66 | 67 | //Method to retrieve only published item of a collection - Check on status entry 68 | 69 | /** 70 | * @return MongoCollection 71 | */ 72 | public function getPublished() 73 | { 74 | return $this->filter(function ($col) { 75 | if ($col->status === 'published') { 76 | return true; 77 | } else { 78 | return false; 79 | } 80 | }); 81 | } 82 | 83 | //Method to retrieve only public item of a collection - Check on status entry 84 | 85 | /** 86 | * @return MongoCollection 87 | */ 88 | public function getPublic() 89 | { 90 | return $this->filter(function ($col) { 91 | if ($col->visibility === 'public') { 92 | return true; 93 | } else { 94 | return false; 95 | } 96 | }); 97 | } 98 | 99 | //Check if the collection has an item with ref_id equal to id of the obj pass in to the parameter, useful to mark a category already selected in edit 100 | 101 | /** 102 | * @param $obj 103 | * @return bool 104 | */ 105 | public function hasItem($obj) 106 | { 107 | if (is_null($obj)) { 108 | return false; 109 | } elseif (is_null($obj->id)) { 110 | return false; 111 | } 112 | 113 | $id = $obj->id; 114 | 115 | $out = $this->filter(function ($col) use ($id) { 116 | if ($col->ref_id === $id) { 117 | return true; 118 | } else { 119 | return false; 120 | } 121 | }); 122 | 123 | if ($out->count() > 0) { 124 | return true; 125 | } else { 126 | return false; 127 | } 128 | } 129 | 130 | //Move the item with ref_id equal to the parameter, useful for edit primary category 131 | 132 | /** 133 | * @param $id 134 | * @return $this 135 | */ 136 | public function moveFirst($id) 137 | { 138 | for ($i = 0; $i <= ($this->count() - 1); $i++) { 139 | $this[$i]->ref_id == $id ? $this->prepend($this->splice($i, 1)[0]) : 0; 140 | } 141 | 142 | return $this; 143 | } 144 | 145 | /** 146 | * @return MongoCollection 147 | */ 148 | public function getActive() 149 | { 150 | return $this->filter(function ($col) { 151 | return $col->is_active; 152 | }); 153 | } 154 | 155 | /** 156 | * @return bool 157 | */ 158 | public function exist() 159 | { 160 | if ($this->count() > 0) { 161 | return true; 162 | } else { 163 | return false; 164 | } 165 | } 166 | 167 | /** 168 | * @param string $aid 169 | * @return mixed 170 | */ 171 | public function findByAID(string $aid) 172 | { 173 | return $this->filter(function ($col) use ($aid) { 174 | return $col->autoincrement_id == $aid; 175 | })->first(); 176 | } 177 | 178 | /** 179 | * @param $id 180 | * @return bool 181 | */ 182 | public function hasPermission($id) 183 | { 184 | if (is_null($id)) { 185 | return false; 186 | } 187 | 188 | $out = $this->filter(function ($col) use ($id) { 189 | if ($col->ref_id == $id) { 190 | return true; 191 | } 192 | }); 193 | 194 | if ($out->count() > 0) { 195 | return true; 196 | } else { 197 | return false; 198 | } 199 | } 200 | 201 | /** 202 | * @param $name 203 | * @return bool 204 | */ 205 | public function hasRole($name) 206 | { 207 | if (is_null($name)) { 208 | return false; 209 | } 210 | 211 | $out = $this->filter(function ($col) use ($name) { 212 | if ($col->name == $name) { 213 | return true; 214 | } 215 | }); 216 | 217 | if ($out->count() > 0) { 218 | return true; 219 | } else { 220 | return false; 221 | } 222 | } 223 | 224 | /** 225 | * @param $name 226 | * @return bool 227 | */ 228 | public function checkPermission($name) 229 | { 230 | if (is_null($name)) { 231 | return false; 232 | } 233 | 234 | $out = $this->filter(function ($col) use ($name) { 235 | if ($col->name == $name) { 236 | return true; 237 | } 238 | }); 239 | 240 | if ($out->count() > 0) { 241 | return true; 242 | } else { 243 | return false; 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/Http/Models/DefaultGeo.php: -------------------------------------------------------------------------------- 1 | id; 29 | } 30 | 31 | /** 32 | * @return string 33 | */ 34 | public function getCollection() 35 | { 36 | return $this->collection; 37 | } 38 | 39 | /** 40 | * @param int $size 41 | * @return mixed 42 | */ 43 | public function getRandom($size = 3) 44 | { 45 | return $this->all()->random($size); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Http/Models/MSModel.php: -------------------------------------------------------------------------------- 1 | app->bind('command.model-doc:generate', GenerateModelDocumentation::class); 22 | $this->app->bind('command.drop:collection', DropCollection::class); 23 | 24 | $this->commands([ 25 | 'command.model-doc:generate', 26 | ]); 27 | 28 | $this->commands([ 29 | 'command.drop:collection', 30 | ]); 31 | } 32 | 33 | /** 34 | * Register the application services. 35 | * 36 | * @return void 37 | */ 38 | public function register() 39 | { 40 | $this->mergeConfigFrom( 41 | $this->packagePath('config/laravel-mongo-auto-sync.php'), 42 | 'laravel-mongo-auto-sync' 43 | ); 44 | } 45 | 46 | /** 47 | * @param $path 48 | * @return string 49 | */ 50 | private function packagePath($path) 51 | { 52 | return __DIR__.'/../'.$path; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Traits/Helper.php: -------------------------------------------------------------------------------- 1 | validateOptions($options); 19 | 20 | return $this->getFieldTypeOptionsValue($options, 'is-array', 'boolean'); 21 | } 22 | 23 | /** 24 | * @param $options 25 | * @return bool|mixed 26 | * 27 | * @throws Exception 28 | */ 29 | public function isCarbonDate($options) 30 | { 31 | $this->validateOptions($options); 32 | 33 | return $this->getFieldTypeOptionsValue($options, 'is-carbon-date', 'boolean'); 34 | } 35 | 36 | /** 37 | * @param $options 38 | * 39 | * @throws Exception 40 | */ 41 | private function validateOptions($options) 42 | { 43 | if (gettype($options) !== 'array') { 44 | throw new Exception($options.' is not a valid array!'); 45 | } 46 | } 47 | 48 | /** 49 | * @param $value 50 | * @param string $expected 51 | * 52 | * @throws Exception 53 | */ 54 | private function validateOptionValue($value, string $expected) 55 | { 56 | if (gettype($value) !== $expected) { 57 | throw new Exception($value.' is not a valid '.$expected.' found '.gettype($value).'! Check on your model configurations.'); 58 | } 59 | } 60 | 61 | /** 62 | * @param array $options 63 | * @param string $key 64 | * @param string $expected 65 | * @return bool|mixed 66 | * 67 | * @throws Exception 68 | */ 69 | private function getFieldTypeOptionsValue(array $options, string $key, string $expected) 70 | { 71 | if (Arr::has($options, $key)) { 72 | $value = Arr::get($options, $key); 73 | $this->validateOptionValue($value, $expected); 74 | 75 | return $value; 76 | } else { 77 | return false; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Traits/MainMongoTrait.php: -------------------------------------------------------------------------------- 1 | initDataForSync($request, $additionalData, $options, $target_additional_data); 31 | $this->storeEditAllItems($request, 'add', $options); 32 | $this->processAllRelationships($request, 'add', '', '', $options); 33 | 34 | //Dispatch the creation event 35 | $this->fireModelEvent('storeWithSync'); 36 | 37 | return $this->fresh(); 38 | } 39 | 40 | /** 41 | * @param Request $request 42 | * @param array $additionalData 43 | * @param array $options 44 | * @param array $target_additional_data 45 | * @return $this 46 | * 47 | * @throws Exception 48 | */ 49 | public function updateWithSync(Request $request, array $additionalData = [], array $options = [], array $target_additional_data = []) 50 | { 51 | $this->initDataForSync($request, $additionalData, $options, $target_additional_data); 52 | $this->storeEditAllItems($request, 'update', $options); 53 | $this->processAllRelationships($request, 'update', '', '', $options); 54 | 55 | //Dispatch the update event 56 | $this->fireModelEvent('updateWithSync'); 57 | 58 | return $this->fresh(); 59 | } 60 | 61 | /** 62 | * @return $this 63 | */ 64 | public function destroyWithSync() 65 | { 66 | //Get the relation info 67 | $relations = $this->getMongoRelation(); 68 | //Process all relationships 69 | foreach ($relations as $method => $relation) { 70 | //Get Relation Save Mode 71 | $type = $relation['type']; 72 | $hasTarget = hasTarget($relation); 73 | if ($hasTarget) { 74 | $modelTarget = $relation['modelTarget']; 75 | $methodOnTarget = $relation['methodOnTarget']; 76 | $modelOnTarget = $relation['modelOnTarget']; 77 | $is_EO = is_EO($type); 78 | $is_EM = is_EM($type); 79 | $is_HO = is_HO($type); 80 | $is_HM = is_HM($type); 81 | $typeOnTarget = getTypeOnTarget($relation); 82 | $is_EM_target = is_EM($typeOnTarget); 83 | $is_EO_target = is_EO($typeOnTarget); 84 | if ($is_EO || $is_EM) {//EmbedsOne Create - EmbedsMany Create 85 | //Delete EmbedsMany or EmbedsOne on Target 86 | $this->deleteTargetObj($method, $modelTarget, $methodOnTarget, $is_EO, $is_EM, $is_EO_target, $is_EM_target); 87 | } 88 | //TODO: Need to be implemented 89 | /* elseif ($is_HM) {//HasMany 90 | } elseif ($is_HO) {//HasOne Create 91 | }*/ 92 | } 93 | } 94 | //Delete current object 95 | $this->delete(); 96 | //Dispatch the destroy event 97 | $this->fireModelEvent('destroyWithSync'); 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * @param array $options 104 | * @param string $key 105 | * @return bool|mixed 106 | */ 107 | private function getOptionValue(array $options, string $key) 108 | { 109 | return Arr::has($options, $key) ? $options[$key] : ''; 110 | } 111 | 112 | /** 113 | * @param $obj 114 | * @param string $EOkey 115 | * @param string $method 116 | * @param string $model 117 | * 118 | * @throws Exception 119 | */ 120 | public function checkPropertyExistence($obj, string $EOkey, $method = '', $model = '') 121 | { 122 | if (! property_exists($obj, $EOkey)) { 123 | $msg = 'Error - '.$EOkey.' attribute not found on obj '.json_encode($obj).' during save of model: '.$model.' and attribute: '.$method; 124 | throw new Exception($msg); 125 | } 126 | } 127 | 128 | /** 129 | * @param $arr 130 | * @param string $key 131 | * 132 | * @throws Exception 133 | */ 134 | public function checkArrayExistence($arr, string $key) 135 | { 136 | if (! Arr::has($arr, $key)) { 137 | $msg = ('Error - '.$key.' attribute not found on obj '.json_encode($arr)); 138 | throw new Exception($msg); 139 | } 140 | } 141 | 142 | /** 143 | * @param Request $request 144 | * @param string $key 145 | * 146 | * @throws Exception 147 | */ 148 | private function checkRequestExistence(Request $request, string $key) 149 | { 150 | if (! $request->has($key)) { 151 | $msg = ('Error - '.$key.' attribute not found in Request '.json_encode($request->all())); 152 | throw new Exception($msg); 153 | } 154 | } 155 | 156 | /** 157 | * @param bool $request_has_key 158 | * @param bool $hasTarget 159 | * @return bool 160 | */ 161 | public function getIsSkippable($request_has_key, $hasTarget = false) 162 | { 163 | return ! $request_has_key && $this->getHasPartialRequest() && ! $hasTarget; 164 | } 165 | 166 | /** 167 | * @return bool 168 | */ 169 | public function getHasPartialRequest() 170 | { 171 | return $this->has_partial_request; 172 | } 173 | 174 | public function setHasPartialRequest(): void 175 | { 176 | $this->has_partial_request = $this->getOptionValue( 177 | $this->getOptions(), 178 | 'request_type' 179 | ) == 'partial'; 180 | } 181 | 182 | /** 183 | * @param string $modelTarget 184 | * @param stdClass $obj 185 | * @return MDModel|null 186 | * 187 | * @throws Exception 188 | */ 189 | private function getModelTobeSync(string $modelTarget, stdClass $obj) 190 | { 191 | $this->checkPropertyExistence($obj, 'ref_id'); 192 | $target_id = $obj->ref_id; 193 | 194 | //Init the Target Model 195 | $modelToBeSync = new $modelTarget; 196 | 197 | return $modelToBeSync->find($target_id); 198 | } 199 | 200 | /** 201 | * @param string $key 202 | * @param Request $request 203 | * @return mixed 204 | * 205 | * @throws Exception 206 | */ 207 | private function getRelationshipRequest(string $key, Request $request) 208 | { 209 | $this->checkRequestExistence( 210 | $request, 211 | $key 212 | ); 213 | 214 | return $request->input($key); 215 | } 216 | 217 | /** 218 | * @return Request 219 | */ 220 | public function getRequest() 221 | { 222 | return $this->request; 223 | } 224 | 225 | /** 226 | * @param Request $request 227 | * @param array $additionalData 228 | */ 229 | public function setRequest(Request $request, array $additionalData): void 230 | { 231 | $request = $request->merge($additionalData); 232 | $this->request = $request; 233 | } 234 | 235 | /** 236 | * @return Request 237 | */ 238 | public function getPartialGeneratedRequest() 239 | { 240 | return $this->partial_generated_request; 241 | } 242 | 243 | /** 244 | * @param array $arr 245 | */ 246 | public function setPartialGeneratedRequest(array $arr): void 247 | { 248 | $partial_generated_request = new Request; 249 | $partial_generated_request->merge($arr); 250 | 251 | $this->partial_generated_request = $partial_generated_request; 252 | } 253 | 254 | /** 255 | * @return array 256 | */ 257 | public function getOptions() 258 | { 259 | return $this->options; 260 | } 261 | 262 | /** 263 | * @param array $options 264 | */ 265 | public function setOptions(array $options): void 266 | { 267 | $this->options = $options; 268 | } 269 | 270 | /** 271 | * @return array 272 | */ 273 | public function getTargetAdditionalData() 274 | { 275 | return $this->target_additional_data; 276 | } 277 | 278 | /** 279 | * @param array $target_additional_data 280 | */ 281 | public function setTargetAdditionalData($target_additional_data): void 282 | { 283 | $this->target_additional_data = $target_additional_data; 284 | } 285 | 286 | /** 287 | * @param Request $request 288 | * @param array $additionalData 289 | * @param array $options 290 | * @param array $target_additional_data 291 | */ 292 | public function initDataForSync(Request $request, array $additionalData, array $options, array $target_additional_data) 293 | { 294 | $this->setRequest($request, $additionalData); 295 | $this->setTargetAdditionalData($target_additional_data); 296 | $this->setOptions($options); 297 | $this->setHasPartialRequest(); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/Traits/ModelAdditionalMethod.php: -------------------------------------------------------------------------------- 1 | items; 28 | } 29 | 30 | /** 31 | * @return array 32 | */ 33 | public function getMongoRelation(): array 34 | { 35 | if (! empty($this->mongoRelation)) { 36 | return $this->mongoRelation; 37 | } else { 38 | return []; 39 | } 40 | } 41 | 42 | /** 43 | * @return void 44 | * 45 | * @throws Exception 46 | */ 47 | public function setMiniModels() 48 | { 49 | $miniModelList = $this->getUniqueMiniModelList(); 50 | $this->mini_models = $this->populateMiniModels($miniModelList); 51 | } 52 | 53 | /** 54 | * @return array 55 | */ 56 | public function getMiniModels() 57 | { 58 | return $this->mini_models; 59 | } 60 | 61 | /** 62 | * @return array 63 | * 64 | * @throws Exception 65 | */ 66 | public function getUniqueMiniModelList() 67 | { 68 | $relationships = $this->getMongoRelation(); 69 | 70 | $models = []; 71 | $embedded_object = []; 72 | 73 | foreach ($relationships as $method => $relationship) { 74 | $hasTarget = hasTarget($relationship); 75 | if ($hasTarget) { 76 | $relationshipsContainsTarget = Arr::has($relationship, 'modelOnTarget'); 77 | if ($relationshipsContainsTarget) { 78 | $models[] = Arr::get($relationship, 'modelOnTarget'); 79 | $embedded_object[$method] = $this->getObjWithRefId($method, $relationship); 80 | } else { 81 | throw new Exception('modelOnTarget not found on relationship '.$method.' array. Check your Model configuration '.get_class($this)); 82 | } 83 | } 84 | } 85 | $this->setPartialGeneratedRequest($embedded_object); 86 | 87 | return collect($models)->unique()->toArray(); 88 | } 89 | 90 | /** 91 | * @param array $miniModelList 92 | * @return mixed 93 | * 94 | * @throws Exception 95 | */ 96 | public function populateMiniModels(array $miniModelList) 97 | { 98 | $miniModels = []; 99 | foreach ($miniModelList as $miniModel) { 100 | $miniModels[$miniModel] = $this->getFreshMiniModel($miniModel); 101 | } 102 | 103 | return $miniModels; 104 | } 105 | 106 | /** 107 | * @param string $mini_model_path 108 | * @return MDModel 109 | * 110 | * @throws Exception 111 | */ 112 | public function getFreshMiniModel(string $mini_model_path) 113 | { 114 | $embededModel = $this->getModelInstanceFromPath($mini_model_path); 115 | $items = $embededModel->getItems(); 116 | foreach ($items as $key => $item) { 117 | $embededModel->$key = $this->castValueToBeSaved($key, $item, $mini_model_path); 118 | } 119 | 120 | return $embededModel; 121 | } 122 | 123 | /** 124 | * @param string $key 125 | * @param $item 126 | * @param string $mini_model_path 127 | * @return array|mixed|UTCDateTime|null 128 | * 129 | * @throws Exception 130 | */ 131 | public function castValueToBeSaved(string $key, $item, string $mini_model_path) 132 | { 133 | $is_ML = isML($item); 134 | $is_MD = isMD($item); 135 | $is_array = $this->isArray($item); 136 | $is_carbon_date = $this->isCarbonDate($item); 137 | 138 | $value = $this->getObjValueToBeSaved($key, $mini_model_path); 139 | if ($is_ML) { 140 | return is_array($value) ? $value : ml([], $value); 141 | } elseif ($is_MD) { 142 | if ($value instanceof UTCDateTime) { 143 | return $value; 144 | } 145 | 146 | if ($value == '') { 147 | return null; 148 | } 149 | 150 | return new UTCDateTime(new DateTime($value)); 151 | } elseif ($is_carbon_date) { 152 | if ($value == '') { 153 | return new UTCDateTime(); 154 | } 155 | 156 | return new UTCDateTime($value); 157 | } elseif ($is_array) { 158 | return is_null($value) ? [] : (is_array($value) ? $value : $value->getAttributes()); 159 | } else { 160 | return $value; 161 | } 162 | } 163 | 164 | /** 165 | * @param string $mini_model_path 166 | * @return MDModel 167 | */ 168 | public function getModelInstanceFromPath(string $mini_model_path) 169 | { 170 | return new $mini_model_path; 171 | } 172 | 173 | /** 174 | * @param string $key 175 | * @param string $mini_model_path 176 | * @param bool $rewrite_ref_id_key 177 | * @return mixed 178 | */ 179 | public function getObjValueToBeSaved(string $key, string $mini_model_path, $rewrite_ref_id_key = true) 180 | { 181 | $key = $key === 'ref_id' && $rewrite_ref_id_key ? '_id' : $key; 182 | $target_additional_data = $this->getTargetAdditionalData(); 183 | $request = $this->getRequest(); 184 | 185 | $db_value = $this->getDbValue($key); 186 | 187 | return Arr::has($target_additional_data, $mini_model_path.'.'.$key) ? Arr::get($target_additional_data, $mini_model_path.'.'.$key) : // Search on target_additional_data [] 4th parameter of updateWithSync() / storeWithSync() 188 | ($request->has($key) ? $request->input($key) : $db_value); // Search on Main Request 1st parameter of updateWithSync() / storeWithSync() or directly on database 189 | //TODO: Add default value from Item Model 190 | } 191 | 192 | /** 193 | * @param string $key 194 | * @return mixed 195 | */ 196 | private function getDbValue(string $key) 197 | { 198 | return $this->$key; 199 | } 200 | 201 | /** 202 | * @param string $key 203 | * @return array 204 | * 205 | * @throws Exception 206 | */ 207 | public function getEmbedModel(string $key) 208 | { 209 | $embedModels = $this->getMiniModels(); 210 | 211 | if (Arr::has($embedModels, $key)) { 212 | return Arr::get($embedModels, $key); 213 | } else { 214 | throw new Exception('I cannot find an embedded model with key: '.$key.'. Check on your model configuration'); 215 | } 216 | } 217 | 218 | /** 219 | * @param string $method 220 | * @param array $relationship 221 | * @return false|string 222 | * 223 | * @throws Exception 224 | */ 225 | public function getObjWithRefId(string $method, array $relationship) 226 | { 227 | $objs = []; 228 | $type = $relationship['type']; 229 | 230 | $is_EO = is_EO($type); 231 | $is_EM = is_EM($type); 232 | 233 | if ($is_EO) { 234 | $objs[] = $this->getObjValueToBeSaved($method, '', false); 235 | } elseif ($is_EM) { 236 | if (! is_null($this->$method) > 0) { 237 | foreach ($this->$method as $value) { 238 | $obj = new stdClass; 239 | $obj->ref_id = $value->ref_id; 240 | $objs[] = $obj; 241 | } 242 | } 243 | } else { 244 | throw new Exception('Relationship '.$method.' type '.$type.' is not valid! Possible values are: EmbedsMany and EmbedsOne'); 245 | } 246 | 247 | return json_encode($objs); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/Traits/PlainMongoTrait.php: -------------------------------------------------------------------------------- 1 | getItems(); 23 | 24 | //Current Obj Create 25 | foreach ($items as $key => $item) { 26 | $is_ML = isML($item); 27 | $is_MD = isMD($item); 28 | 29 | $is_fillable = isFillable($item, $event); 30 | $is_skippable = $this->getIsSkippable($request->has($key)); 31 | 32 | if ($is_skippable) { 33 | continue; 34 | } else { 35 | $this->checkRequestExistence( 36 | $request, 37 | $key 38 | ); 39 | } 40 | 41 | if ($is_fillable) { 42 | if ($is_ML) { 43 | if (is_null($this->$key)) { 44 | $old_value = []; 45 | } else { 46 | $old_value = $this->$key; 47 | } 48 | $this->$key = ml($old_value, $request->input($key)); 49 | } elseif ($is_MD) { 50 | if ($request->input($key) == '' || $request->input($key) == null) { 51 | $this->$key = null; 52 | } else { 53 | $this->$key = new UTCDateTime(new DateTime($request->input($key))); 54 | } 55 | } else { 56 | $this->$key = $request->input($key); 57 | } 58 | } 59 | } 60 | 61 | $this->save(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Traits/RelationshipMongoTrait.php: -------------------------------------------------------------------------------- 1 | setIsPartialRequest($options); 27 | $this->setMiniModels(); // For target Sync 28 | 29 | //Get the relation info 30 | $relations = $this->getMongoRelation(); 31 | 32 | //Process all relationships 33 | foreach ($relations as $method => $relation) { 34 | //Get Relation Save Mode 35 | $type = $relation['type']; 36 | $model = $relation['model']; 37 | $hasTarget = hasTarget($relation); 38 | if ($hasTarget) { 39 | $modelTarget = $relation['modelTarget']; 40 | $methodOnTarget = $relation['methodOnTarget']; 41 | $modelOnTarget = $relation['modelOnTarget']; 42 | $typeOnTarget = getTypeOnTarget($relation); 43 | } else { 44 | $modelTarget = ''; 45 | $methodOnTarget = ''; 46 | $modelOnTarget = ''; 47 | $typeOnTarget = ''; 48 | } 49 | 50 | $is_EO = is_EO($type); 51 | $is_EM = is_EM($type); 52 | 53 | $is_EM_target = is_EM($typeOnTarget); 54 | $is_EO_target = is_EO($typeOnTarget); 55 | 56 | $key = $parent.$method.$counter; 57 | $is_skippable = $this->getIsSkippable($request->has($key), $hasTarget); 58 | 59 | if ($is_skippable) { 60 | continue; 61 | } 62 | $current_request = $request->has($key) ? $request : $this->getPartialGeneratedRequest(); 63 | 64 | $value = $this->getRelationshipRequest($key, $current_request); 65 | 66 | $is_embeds_has_to_be_updated = $request->has($key); 67 | 68 | if (! is_null($value) && ! ($value == '') && ! ($value == '[]')) { 69 | $objs = json_decode($value); 70 | } else { 71 | $objs = getArrayWithEmptyObj($model, $is_EO, $is_EM); 72 | } 73 | 74 | if ($is_EO || $is_EM) {//EmbedsOne Create - EmbedsMany Create 75 | if ($event == 'update' && $is_embeds_has_to_be_updated) { 76 | //Delete EmbedsMany or EmbedsOne on Target - TODO: check if it is necessary to run deleteTargetObj method 77 | if ($hasTarget) { 78 | $this->deleteTargetObj($method, $modelTarget, $methodOnTarget, $is_EO, $is_EM, $is_EO_target, $is_EM_target); 79 | } 80 | //Delete EmbedsMany or EmbedsOne on current object 81 | if ($is_EM) { 82 | $this->$method = []; 83 | $this->save(); 84 | } 85 | } 86 | 87 | if (! empty($objs)) { 88 | if ($is_EM) { 89 | $this->tempEM = []; 90 | } 91 | 92 | $i = 0; 93 | foreach ($objs as $obj) { 94 | $this->processOneEmbeddedRelationship( 95 | $request, 96 | $obj, 97 | $type, 98 | $model, 99 | $method, 100 | $modelTarget, 101 | $methodOnTarget, 102 | $modelOnTarget, $event, 103 | $hasTarget, 104 | $is_EO, 105 | $is_EM, 106 | $is_EO_target, 107 | $is_EM_target, 108 | $i, 109 | $is_embeds_has_to_be_updated, 110 | $options); 111 | $i++; 112 | } 113 | 114 | if ($is_EM) { 115 | $this->$method = $this->tempEM; 116 | } 117 | } else { 118 | $this->$method = []; 119 | } 120 | $this->save(); 121 | } 122 | } 123 | } 124 | 125 | /** 126 | * @param $mini_model 127 | * @param string $method_on_target 128 | * @param bool $is_EO_target 129 | * @param bool $is_EM_target 130 | * 131 | * @throws \Throwable 132 | */ 133 | public function updateRelationWithSync($mini_model, string $method_on_target, $is_EO_target, $is_EM_target) 134 | { 135 | if ($is_EM_target) { 136 | $new_values = []; 137 | throw_if( 138 | ! isset($this->$method_on_target), 139 | new Exception( 140 | 'Error during target update. Remember to init the attribute '.$method_on_target. 141 | ' on collection '.$this->getCollection() 142 | ) 143 | ); 144 | 145 | $is_update_operation = false; 146 | 147 | foreach ($this->$method_on_target as $temp) { 148 | throw_if( 149 | is_array($temp), 150 | new Exception( 151 | 'Error during target update. Remember to declare '.$method_on_target.' as '. 152 | 'EmbedsMany relationship on model '.get_class($this) 153 | ) 154 | ); 155 | 156 | if (! is_null($temp)) { 157 | if ($this->getIsPartialRequest()) { 158 | if (Arr::get($temp->attributes, 'ref_id') !== Arr::get($mini_model->attributes, 'ref_id')) { 159 | $new_values[] = $temp->attributes; 160 | } else { 161 | $new_values[] = $mini_model->attributes; 162 | $is_update_operation = true; 163 | } 164 | } else { 165 | $new_values[] = $temp->attributes; 166 | } 167 | } 168 | } 169 | 170 | if (! $is_update_operation) { 171 | $new_values[] = $mini_model->attributes; 172 | } 173 | } elseif ($is_EO_target) { 174 | throw_if( 175 | is_array($mini_model), 176 | new Exception( 177 | 'Error during target update. Remember to declare '.$method_on_target.' as '. 178 | 'EmbedOne relationship on model '.get_class($this) 179 | ) 180 | ); 181 | $new_values = $mini_model->attributes; 182 | } 183 | 184 | $this->$method_on_target = $new_values; 185 | $this->save(); 186 | } 187 | 188 | /** 189 | * @param Request $request 190 | * @param $obj 191 | * @param $type 192 | * @param $model 193 | * @param $method 194 | * @param $modelTarget 195 | * @param $methodOnTarget 196 | * @param $modelOnTarget 197 | * @param $event 198 | * @param $hasTarget 199 | * @param bool $is_EO 200 | * @param bool $is_EM 201 | * @param bool $is_EO_target 202 | * @param bool $is_EM_target 203 | * @param $i 204 | * @param bool $is_embeds_has_to_be_updated 205 | * @param $options 206 | * 207 | * @throws Exception 208 | */ 209 | public function processOneEmbeddedRelationship(Request $request, $obj, $type, $model, $method, $modelTarget, $methodOnTarget, $modelOnTarget, $event, $hasTarget, $is_EO, $is_EM, $is_EO_target, $is_EM_target, $i, $is_embeds_has_to_be_updated, $options) 210 | { 211 | if ($is_embeds_has_to_be_updated) { 212 | $this->processEmbedOnCurrentCollection($request, $obj, $type, $model, $method, $event, $is_EO, $is_EM, $i, $options); 213 | } 214 | 215 | if ($hasTarget) { 216 | $this->processEmbedOnTargetCollection($modelTarget, $obj, $methodOnTarget, $modelOnTarget, $is_EO_target, $is_EM_target); 217 | } 218 | } 219 | 220 | /** 221 | * @param string $method 222 | * @param string $modelTarget 223 | * @param string $methodOnTarget 224 | * @param bool $is_EO 225 | * @param bool $is_EM 226 | * @param bool $is_EO_target 227 | * @param bool $is_EM_target 228 | */ 229 | public function deleteTargetObj($method, $modelTarget, $methodOnTarget, bool $is_EO, bool $is_EM, bool $is_EO_target, bool $is_EM_target) 230 | { 231 | if ($is_EO) { 232 | $embedObj = $this->$method; 233 | if (! is_null($embedObj)) { 234 | $target_id = $embedObj->ref_id; 235 | $this->handleSubTarget($target_id, $modelTarget, $methodOnTarget, $is_EO_target, $is_EM_target); 236 | } 237 | } elseif ($is_EM) { 238 | foreach ($this->$method as $target) { 239 | $this->handleSubTarget($target->ref_id, $modelTarget, $methodOnTarget, $is_EO_target, $is_EM_target); 240 | } 241 | } 242 | } 243 | 244 | /** 245 | * @param string|null $target_id 246 | * @param string $modelTarget 247 | * @param string $methodOnTarget 248 | * @param bool $is_EO_target 249 | * @param bool $is_EM_target 250 | */ 251 | public function handleSubTarget(?string $target_id, string $modelTarget, string $methodOnTarget, bool $is_EO_target, bool $is_EM_target) 252 | { 253 | if ($is_EM_target) { 254 | $target = new $modelTarget; 255 | $target = $target->all()->where('id', $target_id)->first(); 256 | if (! is_null($target)) { 257 | $new_values = []; 258 | foreach ($target->$methodOnTarget as $temp) { 259 | if ($temp->ref_id !== $this->getId()) { 260 | $new_values[] = $temp->attributes; 261 | } 262 | } 263 | $target->$methodOnTarget = $new_values; 264 | $target->save(); 265 | } 266 | } elseif ($is_EO_target) { 267 | //Do nothing because when we are updating we already init the informations 268 | } 269 | } 270 | 271 | /** 272 | * @param Request $request 273 | * @param $obj 274 | * @param $type 275 | * @param $model 276 | * @param $method 277 | * @param $event 278 | * @param $is_EO 279 | * @param $is_EM 280 | * @param $i 281 | * @param $options 282 | * 283 | * @throws Exception 284 | */ 285 | private function processEmbedOnCurrentCollection(Request $request, $obj, $type, $model, $method, $event, $is_EO, $is_EM, $i, $options) 286 | { 287 | //Init the embed one model 288 | $embedObj = new $model; 289 | 290 | $EOitems = $embedObj->getItems(); 291 | //Current Obj Create 292 | foreach ($EOitems as $EOkey => $item) { 293 | if (! is_null($obj)) { 294 | $is_ML = isML($item); 295 | $is_MD = isMD($item); 296 | $this->checkPropertyExistence($obj, $EOkey, $method, $model); 297 | 298 | if ($is_ML) { 299 | $embedObj->$EOkey = ml([], $obj->$EOkey); 300 | } elseif ($EOkey == 'updated_at' || $EOkey == 'created_at') { 301 | $embedObj->$EOkey = now(); 302 | } elseif ($is_MD) { 303 | if ($obj->$EOkey == '' || $obj->$EOkey == null) { 304 | $embedObj->$EOkey = null; 305 | } else { 306 | $embedObj->$EOkey = new UTCDateTime(new DateTime($obj->$EOkey)); 307 | } 308 | } else { 309 | $embedObj->$EOkey = $obj->$EOkey; 310 | } 311 | } 312 | } 313 | 314 | //else if($is_EM){//To be implemented} 315 | //else if($is_HM){//To be implemented} 316 | //else if($is_HO){//To be implemented} 317 | 318 | //Get counter for embeds many with level > 1 319 | $counter = getCounterForRelationships($method, $is_EO, $is_EM, $i); 320 | //Check for another Level of Relationship 321 | $embedObj->processAllRelationships($request, $event, $method.'-', $counter, $options); 322 | 323 | if ($is_EO) { 324 | $this->$method = $embedObj->attributes; 325 | } else { 326 | $this->tempEM[] = $embedObj->attributes; 327 | } 328 | } 329 | 330 | /** 331 | * @param $modelTarget 332 | * @param $obj 333 | * @param $methodOnTarget 334 | * @param $modelOnTarget 335 | * @param bool $is_EO_target 336 | * @param bool $is_EM_target 337 | * 338 | * @throws Exception 339 | */ 340 | private function processEmbedOnTargetCollection($modelTarget, $obj, $methodOnTarget, $modelOnTarget, bool $is_EO_target, bool $is_EM_target) 341 | { 342 | $modelToBeSync = $this->getModelTobeSync($modelTarget, $obj); 343 | if (! is_null($modelToBeSync)) { 344 | $miniModel = $this->getEmbedModel($modelOnTarget); 345 | $modelToBeSync->setIsPartialRequest([], $this->getIsPartialRequest()); 346 | $modelToBeSync->updateRelationWithSync($miniModel, $methodOnTarget, $is_EO_target, $is_EM_target); 347 | //TODO:Sync target on level > 1 348 | //$modelToBeSync->processAllRelationships($request, $event, $methodOnTarget, $methodOnTarget . "-"); 349 | } 350 | } 351 | 352 | public function getIsPartialRequest() 353 | { 354 | return $this->is_partial_request; 355 | } 356 | 357 | public function setIsPartialRequest(array $options, $is_partial_request = null): void 358 | { 359 | if (! is_null($is_partial_request)) { 360 | $this->is_partial_request = $is_partial_request; 361 | 362 | return; 363 | } 364 | 365 | if (Arr::has($options, 'request_type')) { 366 | $this->is_partial_request = Arr::get($options, 'request_type') == 'partial'; 367 | 368 | return; 369 | } 370 | 371 | $this->is_partial_request = false; 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /src/syncUtils.php: -------------------------------------------------------------------------------- 1 | $input]); 50 | } 51 | } 52 | 53 | if (! function_exists('isML')) { 54 | function isML($value) 55 | { 56 | if (array_key_exists('is-ml', $value)) { 57 | return $value['is-ml']; 58 | } else { 59 | return false; 60 | } 61 | } 62 | } 63 | 64 | if (! function_exists('isMD')) { 65 | function isMD($value) 66 | { 67 | if (array_key_exists('is-md', $value)) { 68 | return $value['is-md']; 69 | } else { 70 | return false; 71 | } 72 | } 73 | } 74 | 75 | if (! function_exists('is_EM')) { 76 | function is_EM($value) 77 | { 78 | if ($value === 'EmbedsMany') { 79 | return true; 80 | } else { 81 | return false; 82 | } 83 | } 84 | } 85 | 86 | if (! function_exists('is_EO')) { 87 | function is_EO($value) 88 | { 89 | if ($value === 'EmbedsOne') { 90 | return true; 91 | } else { 92 | return false; 93 | } 94 | } 95 | } 96 | 97 | if (! function_exists('is_HM')) { 98 | function is_HM($value) 99 | { 100 | if ($value === 'HasMany') { 101 | return true; 102 | } else { 103 | return false; 104 | } 105 | } 106 | } 107 | 108 | if (! function_exists('is_HO')) { 109 | function is_HO($value) 110 | { 111 | if ($value === 'HasOne') { 112 | return true; 113 | } else { 114 | return false; 115 | } 116 | } 117 | } 118 | 119 | if (! function_exists('isEditable')) { 120 | function isEditable($value) 121 | { 122 | if (array_key_exists('is-editable', $value)) { 123 | return $value['is-editable']; 124 | } else { 125 | return true; 126 | } 127 | } 128 | } 129 | 130 | if (! function_exists('hasTarget')) { 131 | function hasTarget($value) 132 | { 133 | if (array_key_exists('has-target', $value)) { 134 | return $value['has-target']; 135 | } else { 136 | return true; 137 | } 138 | } 139 | } 140 | 141 | if (! function_exists('isFillable')) { 142 | function isFillable($value, $event) 143 | { 144 | if ($event === 'add') { 145 | return true; 146 | } else { 147 | return isEditable($value); 148 | } 149 | } 150 | } 151 | 152 | if (! function_exists('getAID')) { 153 | /** 154 | * @param Model $model 155 | * @return string 156 | */ 157 | function getAID(Model $model) 158 | { 159 | //Get Last Obj 160 | $obj = $model->orderBy('created_at', 'desc')->first(); 161 | 162 | return is_null($obj) ? 1 : $obj->autoincrement_id + 1; 163 | } 164 | } 165 | 166 | if (! function_exists('getArrayWithEmptyObj')) { 167 | /** 168 | * @param $model 169 | * @return array 170 | */ 171 | function getArrayWithEmptyObj($model, $is_EO, $is_EM) 172 | { 173 | $arr = []; 174 | if ($is_EO) { 175 | $obj = new stdClass(); 176 | $embedObj = new $model; 177 | $EOitems = $embedObj->getItems(); 178 | 179 | //Current Obj Create 180 | foreach ($EOitems as $EOkey => $item) { 181 | $obj->$EOkey = null; 182 | } 183 | 184 | $arr[] = $obj; 185 | }// $is_EM == empty array 186 | 187 | return $arr; 188 | } 189 | } 190 | 191 | if (! function_exists('getCounterForRelationships')) { 192 | /** 193 | * @param $method 194 | * @param $is_EO 195 | * @param $is_EM 196 | * @param $i 197 | * @return string 198 | */ 199 | function getCounterForRelationships($method, $is_EO, $is_EM, $i) 200 | { 201 | if ($method === '' || ($method !== '' && $is_EO) || ($method === '' && $is_EM)) { 202 | return ''; 203 | } else { 204 | return '-'.$i; 205 | } 206 | } 207 | 208 | if (! function_exists('getTypeOnTarget')) { 209 | function getTypeOnTarget($relation) 210 | { 211 | return Arr::has($relation, 'typeOnTarget') ? Arr::get($relation, 'typeOnTarget') : 'EmbedsMany'; 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /tests/BrowserKitTestCase.php: -------------------------------------------------------------------------------- 1 | 'Package\Facade', 18 | ]; 19 | } 20 | 21 | public function setUp() 22 | { 23 | parent::setUp(); 24 | $this->artisan('migrate', ['--database' => 'sqlite']); 25 | $this->loadLaravelMigrations(['--database' => 'sqlite']); 26 | $this->withFactories(__DIR__.'/factories'); 27 | } 28 | 29 | /** 30 | * Creates the application. 31 | * 32 | * Needs to be implemented by subclasses. 33 | * 34 | * @return \Illuminate\Foundation\Application 35 | */ 36 | public function createApplication() 37 | { 38 | $app = parent::createApplication(); 39 | 40 | Hash::setRounds(4); 41 | 42 | return $app; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/DropCollectionTest.php: -------------------------------------------------------------------------------- 1 | prepareArticleData([], 10); 16 | 17 | $this->artisan('drop:collection', ['collection_name' => 'Article']) 18 | ->assertExitCode(0); 19 | 20 | $articles = Article::all(); 21 | $this->assertEmpty($articles); 22 | $category = Category::where('name.'.cl(), 'sport')->first(); 23 | 24 | $this->assertEmpty($category->articles); 25 | 26 | Article::truncate(); 27 | Category::truncate(); 28 | } 29 | 30 | public function test_exception_model_not_found() 31 | { 32 | $this->expectExceptionMessage('Error ModelThatDoesNotExist Model not found'); 33 | $this->artisan('drop:collection', ['collection_name' => 'ModelThatDoesNotExist']) 34 | ->assertExitCode(0); 35 | } 36 | 37 | public function test_exception_path_not_found() 38 | { 39 | config()->set('laravel-mongo-auto-sync.model_path', 'path_that_does_not_exist'); 40 | $this->expectExceptionMessage('Error directory path_that_does_not_exist not found'); 41 | $this->artisan('model-doc:generate', ['collection_name' => 'ModelThatDoesNotExist']) 42 | ->assertExitCode(0); 43 | } 44 | 45 | public function isDeleted($articles) 46 | { 47 | if ($articles != null) { 48 | foreach ($articles as $article) { 49 | $this->assertNull(Article::find($article->id)); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/GenerateModelDocumentationTest.php: -------------------------------------------------------------------------------- 1 | getOutput(); 10 | 11 | /*$this->artisan('model-doc:generate', ['collection_name' => 'Article']) 12 | ->expectsOutput($output) 13 | ->assertExitCode(0);*/ 14 | $this->assertTrue(true); 15 | } 16 | 17 | public function test_generate_model_documentation_for_article() 18 | { 19 | /*$this->artisan('model-doc:generate', ['collection_name' => 'article']) 20 | ->expectsOutput('') 21 | ->assertExitCode(0);*/ 22 | 23 | $this->assertTrue(true); 24 | } 25 | 26 | public function test_exception_model_not_found() 27 | { 28 | $this->expectExceptionMessage('Error ModelThatDoesNotExist Model not found'); 29 | $this->artisan('model-doc:generate', ['collection_name' => 'ModelThatDoesNotExist']) 30 | ->assertExitCode(0); 31 | } 32 | 33 | public function test_exception_path_not_found() 34 | { 35 | config()->set('laravel-mongo-auto-sync.model_path', 'path_that_does_not_exist'); 36 | $this->expectExceptionMessage('Error directory path_that_does_not_exist not found'); 37 | $this->artisan('model-doc:generate', ['collection_name' => 'ModelThatDoesNotExist']) 38 | ->assertExitCode(0); 39 | } 40 | 41 | private function getOutput() 42 | { 43 | return ' 44 | 45 | 46 | 47 | 48 | /** 49 | * 50 | * Plain Fields 51 | * 52 | * @property string $id 53 | * @property string $title 54 | * 55 | * 56 | *'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/HelperTest.php: -------------------------------------------------------------------------------- 1 | true, 14 | ]; 15 | $emptyArray = []; 16 | $article = new Article; 17 | 18 | //Check return true 19 | $outBoolValue = $article->isArray($boolValue); 20 | $this->assertTrue($outBoolValue); 21 | 22 | //Check return false 23 | $outNotBoolValue = $article->isArray($emptyArray); 24 | $this->assertFalse($outNotBoolValue); 25 | 26 | // 27 | $this->expectExceptionMessage($stringValue.' is not a valid array!'); 28 | $article->isArray($stringValue); 29 | } 30 | 31 | public function test_validateOptionValueException() 32 | { 33 | $notBoolValue = [ 34 | 'is-array' => 'value', 35 | ]; 36 | $article = new Article; 37 | $expected = 'boolean'; 38 | $this->expectExceptionMessage($notBoolValue['is-array'].' is not a valid '.$expected.' found '.gettype($notBoolValue['is-array']).'! Check on your model configurations.'); 39 | $article->isArray($notBoolValue); 40 | } 41 | 42 | public function test_isCarbonDate() 43 | { 44 | $stringValue = ''; 45 | $boolValue = [ 46 | 'is-carbon-date' => true, 47 | ]; 48 | $emptyArray = []; 49 | $article = new Article; 50 | 51 | //Check return true 52 | $outBoolValue = $article->isCarbonDate($boolValue); 53 | $this->assertTrue($outBoolValue); 54 | 55 | //Check return false 56 | $outNotBoolValue = $article->isCarbonDate($emptyArray); 57 | $this->assertFalse($outNotBoolValue); 58 | 59 | // 60 | $this->expectExceptionMessage($stringValue.' is not a valid array!'); 61 | $article->isCarbonDate($stringValue); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/MDModelTest.php: -------------------------------------------------------------------------------- 1 | createNavigation(); 13 | 14 | $out = $modelTest->getId(); 15 | 16 | $this->assertNotNull($out); 17 | } 18 | 19 | public function test_getCollection() 20 | { 21 | $navigation = new Navigation(); 22 | 23 | $out = $navigation->getCollection(); 24 | 25 | $this->assertEquals('navigation', $out); 26 | } 27 | 28 | public function test_getRandom() 29 | { 30 | Navigation::truncate(); 31 | 32 | for ($i = 0; $i < 10; $i++) { 33 | $this->createNavigation(); 34 | } 35 | 36 | $navigation = new Navigation(); 37 | $out = $navigation->getRandom(); 38 | $this->assertInstanceOf(MongoCollection::class, $out); 39 | $this->assertCount(3, $out); 40 | 41 | // 42 | 43 | $out = $navigation->getRandom(1); 44 | $this->assertInstanceOf(MongoCollection::class, $out); 45 | $this->assertCount(1, $out); 46 | 47 | Navigation::truncate(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/MainMongoTraitTest.php: -------------------------------------------------------------------------------- 1 | expectException(Exception::class); 16 | 17 | $this->checkPropertyExistence( 18 | (object) ['key' => 'value'], 19 | 'fake_key' 20 | ); 21 | } 22 | 23 | public function test_checkArrayExistence() 24 | { 25 | $this->expectException(Exception::class); 26 | 27 | $this->checkArrayExistence( 28 | ['key' => 'value'], 29 | 'fake_key' 30 | ); 31 | } 32 | 33 | public function test_checkRequestExistence() 34 | { 35 | $request = new Request(); 36 | 37 | $this->expectException(Exception::class); 38 | 39 | $request = $request->replace([ 40 | ['key' => 'value'], 41 | ]); 42 | 43 | $this->checkRequestExistence( 44 | $request, 45 | 'fake_key' 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/ModelAdditionalMethodTest.php: -------------------------------------------------------------------------------- 1 | setRequest(); 34 | 35 | // [] 36 | 37 | $this->setMl([]); 38 | 39 | $parsed_value = $this->castValueToBeSaved('ml', [ 40 | 'is-ml' => true, 41 | ], 'Tests\Models\MiniSubItem'); 42 | 43 | $this->assertIsArray($parsed_value); 44 | $this->assertEquals([], $parsed_value); 45 | 46 | // null 47 | 48 | $this->setMl(null); 49 | 50 | $parsed_value = $this->castValueToBeSaved('ml', [ 51 | 'is-ml' => true, 52 | ], 'Tests\Models\MiniSubItem'); 53 | 54 | $this->assertIsArray($parsed_value); 55 | $this->assertCount(1, $parsed_value); 56 | } 57 | 58 | public function test_cast_md() 59 | { 60 | $this->setRequest(); 61 | 62 | // UTCDateTime 63 | 64 | $this->setMd(new UTCDateTime(new DateTime())); 65 | 66 | $parsed_value = $this->castValueToBeSaved('md', [ 67 | 'is-md' => true, 68 | ], 'Tests\Models\MiniSubItem'); 69 | 70 | $this->assertEquals($this->getMd(), $parsed_value); 71 | 72 | // '' empty string 73 | 74 | $this->setMd(''); 75 | 76 | $parsed_value = $this->castValueToBeSaved('md', [ 77 | 'is-md' => true, 78 | ], 'Tests\Models\MiniSubItem'); 79 | 80 | $this->assertNull($parsed_value); 81 | 82 | // null 83 | 84 | $this->setMd(null); 85 | 86 | $parsed_value = $this->castValueToBeSaved('md', [ 87 | 'is-md' => true, 88 | ], 'Tests\Models\MiniSubItem'); 89 | 90 | $this->assertNull($parsed_value); 91 | 92 | // carbon 93 | 94 | $this->setMd(Carbon::now()); 95 | 96 | $parsed_value = $this->castValueToBeSaved('md', [ 97 | 'is-md' => true, 98 | ], 'Tests\Models\MiniSubItem'); 99 | 100 | $this->assertInstanceOf(UTCDateTime::class, $parsed_value); 101 | } 102 | 103 | public function test_cast_carbon_date() 104 | { 105 | $this->setRequest(); 106 | 107 | // Carbon 108 | 109 | $now = Carbon::now(); 110 | $this->setCarbonDate($now); 111 | $now_utc = new UTCDateTime($now); 112 | 113 | $parsed_value = $this->castValueToBeSaved('carbon_date', [ 114 | 'is-carbon-date' => true, 115 | ], 'Tests\Models\MiniSubItem'); 116 | 117 | $this->assertEquals($now_utc, $parsed_value); 118 | 119 | // '' empty string 120 | 121 | $this->setCarbonDate(''); 122 | 123 | $parsed_value = $this->castValueToBeSaved('carbon_date', [ 124 | 'is-carbon-date' => true, 125 | ], 'Tests\Models\MiniSubItem'); 126 | 127 | $this->assertInstanceOf(UTCDateTime::class, $parsed_value); 128 | 129 | // null 130 | 131 | $this->setCarbonDate(null); 132 | 133 | $parsed_value = $this->castValueToBeSaved('carbon_date', [ 134 | 'is-carbon-date' => true, 135 | ], 'Tests\Models\MiniSubItem'); 136 | 137 | $this->assertInstanceOf(UTCDateTime::class, $parsed_value); 138 | } 139 | 140 | public function test_cast_array() 141 | { 142 | $this->setRequest(); 143 | 144 | // filled array 145 | 146 | $this->setArray(['key' => 'value']); 147 | 148 | $parsed_value = $this->castValueToBeSaved('array', [ 149 | 'is-array' => true, 150 | ], 'Tests\Models\MiniSubItem'); 151 | 152 | $this->assertArrayHasKey('key', $parsed_value); 153 | $this->assertEquals('value', $parsed_value['key']); 154 | 155 | // null 156 | 157 | $this->setArray(null); 158 | 159 | $parsed_value = $this->castValueToBeSaved('array', [ 160 | 'is-array' => true, 161 | ], 'Tests\Models\MiniSubItem'); 162 | 163 | $this->assertIsArray($parsed_value); 164 | $this->assertEquals([], $parsed_value); 165 | 166 | // getAttributes 167 | 168 | $this->setArray($this->createSubItems()); 169 | 170 | $parsed_value = $this->castValueToBeSaved('array', [ 171 | 'is-array' => true, 172 | ], 'Tests\Models\MiniSubItem'); 173 | 174 | $this->assertIsArray($parsed_value); 175 | } 176 | 177 | public function test_unique_mini_model() 178 | { 179 | $this->setMongoRelation([ 180 | 'relation' => [ 181 | 'type' => 'EmbedsMany', 182 | 'mode' => 'classic', 183 | 'model' => 'App\Models\MiniRelation', 184 | 'modelTarget' => 'App\Models\Relation', 185 | 'methodOnTarget' => 'Related', 186 | ], 187 | ]); 188 | 189 | $this->expectException(Exception::class); 190 | 191 | $this->getUniqueMiniModelList(); 192 | } 193 | 194 | public function test_obj_with_ref_id() 195 | { 196 | $this->expectException(Exception::class); 197 | 198 | $this->getObjWithRefId('', [ 199 | 'type' => 'fake', 200 | ]); 201 | } 202 | 203 | public function test_embed_model() 204 | { 205 | $this->setMiniModels([ 206 | 'modelTargets' => 'App\Models\Relation', 207 | ]); 208 | 209 | $this->expectException(Exception::class); 210 | 211 | $this->getEmbedModel('modelTarget'); 212 | } 213 | 214 | /* GETTERs & SETTERs */ 215 | 216 | public function getMl() 217 | { 218 | return $this->ml; 219 | } 220 | 221 | public function setMl($ml): void 222 | { 223 | $this->ml = $ml; 224 | } 225 | 226 | public function getMd() 227 | { 228 | return $this->md; 229 | } 230 | 231 | public function setMd($md): void 232 | { 233 | $this->md = $md; 234 | } 235 | 236 | public function getCarbonDate() 237 | { 238 | return $this->carbon_date; 239 | } 240 | 241 | public function setCarbonDate($carbon_date): void 242 | { 243 | $this->carbon_date = $carbon_date; 244 | } 245 | 246 | public function getArray() 247 | { 248 | return $this->array; 249 | } 250 | 251 | public function setArray($array): void 252 | { 253 | $this->array = $array; 254 | } 255 | 256 | private function setRequest() 257 | { 258 | $this->request = new Request(); 259 | } 260 | 261 | private function setMongoRelation($mongoRelation) 262 | { 263 | $this->mongoRelation = $mongoRelation; 264 | } 265 | 266 | private function setMiniModels($mini_models) 267 | { 268 | $this->mini_models = $mini_models; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /tests/Models/Article.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'title' => [ 12 | 'is-ml' => true, 13 | 'is-editable' => true, 14 | ], 15 | 'content' => [ 16 | 'is-ml' => true, 17 | ], 18 | 'slug' => [ 19 | 'is-ml' => true, 20 | ], 21 | 'visibility' => [], 22 | 'status' => [], 23 | 'is_deleted' => [], 24 | 'is_active' => [], 25 | ]; 26 | 27 | protected $mongoRelation = [ 28 | 'primarycategory' => [ 29 | 'type' => 'EmbedsOne', 30 | 'model' => 'Tests\Models\MiniCategory', 31 | 'has-target' => false, 32 | ], 33 | 'categories' => [ 34 | 'type' => 'EmbedsMany', 35 | 'mode' => 'classic', 36 | 'model' => 'Tests\Models\MiniCategory', 37 | 'modelTarget' => 'Tests\Models\Category', 38 | 'methodOnTarget' => 'articles', 39 | 'modelOnTarget' => 'Tests\Models\MiniArticle', 40 | ], 41 | ]; 42 | 43 | public function categories() 44 | { 45 | return $this->embedsMany('Tests\Models\MiniCategory'); 46 | } 47 | 48 | public function primarycategory() 49 | { 50 | return $this->embedsOne('Tests\Models\MiniCategory'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Models/Category.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [ 12 | 'is-ml' => true, 13 | ], 14 | 'slug' => [ 15 | 'is-ml' => true, 16 | ], 17 | 'description' => [ 18 | 'is-ml' => true, 19 | ], 20 | ]; 21 | 22 | protected $mongoRelation = [ 23 | 'articles' => [ 24 | 'type' => 'EmbedsMany', 25 | 'model' => 'Tests\Models\MiniArticle', 26 | 'mode' => 'classic', 27 | 'modelTarget' => 'Tests\Models\Article', 28 | 'methodOnTarget' => 'categories', 29 | 'modelOnTarget' => 'Tests\Models\MiniCategory', 30 | ], 31 | ]; 32 | 33 | public function articles() 34 | { 35 | return $this->embedsMany('Tests\Models\MiniArticle'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Models/Item.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'code' => [], 12 | 'price' => [], 13 | 'quantity' => [], 14 | 'discount' => [], 15 | 'taxable_price' => [], 16 | 'partial_vat' => [], 17 | 'total_price' => [], 18 | 'vat_code' => [], 19 | 'vat_value' => [], 20 | 'vat_label' => [], 21 | 'collection_type' => [], 22 | 'navigation_code' => [], 23 | ]; 24 | 25 | protected $mongoRelation = [ 26 | 'navigation' => [ 27 | 'type' => 'EmbedsOne', 28 | 'mode' => 'classic', 29 | 'model' => 'Tests\Models\MiniNavigation', 30 | 'modelTarget' => 'Tests\Models\Navigation', 31 | 'methodOnTarget' => 'sub_items', 32 | 'modelOnTarget' => 'Tests\Models\MiniItem', 33 | ], 34 | ]; 35 | 36 | public function navigation() 37 | { 38 | return $this->embedsOne('Tests\Models\MiniNavigation'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Models/MiniArticle.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'title' => [], 12 | 'slug' => [], 13 | 'visibility' => [], 14 | 'status' => [], 15 | 'last_updated_by' => [], 16 | 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /tests/Models/MiniCategory.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [], 12 | 'slug' => [], 13 | 'description' => [], 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /tests/Models/MiniItem.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [], 12 | 'code' => [], 13 | ]; 14 | } 15 | -------------------------------------------------------------------------------- /tests/Models/MiniNavigation.php: -------------------------------------------------------------------------------- 1 | [], 21 | 'code' => [], 22 | 'text' => [], 23 | 'title' => [ 24 | 'is-ml' => true, 25 | ], 26 | ]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/Models/MiniPermission.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [], 12 | 'label' => [ 13 | 'is-ml' => true, 14 | ], 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /tests/Models/MiniRole.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [], 12 | ]; 13 | } 14 | -------------------------------------------------------------------------------- /tests/Models/MiniSubItem.php: -------------------------------------------------------------------------------- 1 | [], 21 | 'text' => [ 22 | 'is-ml' => true, 23 | ], 24 | 'code' => [], 25 | 'href' => [], 26 | ]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/Models/MiniUser.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [], 12 | 'surname' => [], 13 | 'email' => [], 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /tests/Models/Navigation.php: -------------------------------------------------------------------------------- 1 | [], 29 | 'code' => [], 30 | 'href' => [], 31 | 'date' => [ 32 | 'is-carbon-date' => true, 33 | ], 34 | 'target' => [], 35 | 'title' => [ 36 | 'is-ml' => true, 37 | ], 38 | ]; 39 | 40 | protected $mongoRelation = [ 41 | 'sub_items' => [ 42 | 'type' => 'EmbedsMany', 43 | 'mode' => 'classic', 44 | 'model' => 'Tests\Models\MiniSubItem', 45 | 'modelTarget' => 'Tests\Models\SubItem', 46 | 'methodOnTarget' => 'navigation', 47 | 'modelOnTarget' => 'Tests\Models\MiniNavigation', 48 | 'typeOnTarget' => 'EmbedsOne', 49 | ], 50 | ]; 51 | 52 | public function sub_items() 53 | { 54 | return $this->embedsMany('Tests\Models\MiniItem'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Models/Permission.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'label' => [ 12 | 'is-ml' => true, 13 | ], 14 | ]; 15 | 16 | protected $mongoRelation = [ 17 | 'roles' => [ 18 | 'type' => 'EmbedsMany', 19 | 'mode' => 'classic', 20 | 'model' => 'Tests\Models\MiniRole', 21 | 'modelTarget' => 'Tests\Models\Role', 22 | 'methodOnTarget' => 'permissions', 23 | 'modelOnTarget' => 'Tests\Models\MiniPermission', 24 | ], 25 | 'users' => [ 26 | 'type' => 'EmbedsMany', 27 | 'mode' => 'classic', 28 | 'model' => 'Tests\Models\MiniUser', 29 | 'modelTarget' => 'Tests\Models\User', 30 | 'methodOnTarget' => 'permissions', 31 | 'modelOnTarget' => 'Tests\Models\MiniPermission', 32 | ], 33 | ]; 34 | 35 | public function roles() 36 | { 37 | return $this->embedsMany('Tests\Models\Role'); 38 | } 39 | 40 | public function users() 41 | { 42 | return $this->embedsMany('Tests\Models\User'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Models/Role.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'label' => [ 12 | 'is-ml' => true, 13 | ], 14 | ]; 15 | 16 | protected $mongoRelation = [ 17 | 'permissions' => [ 18 | 'type' => 'EmbedsMany', 19 | 'mode' => 'classic', 20 | 'model' => 'Tests\Models\MiniPermission', 21 | 'modelTarget' => 'Tests\Models\Permission', 22 | 'methodOnTarget' => 'roles', 23 | 'modelOnTarget' => 'Tests\Models\MiniRole', 24 | ], 25 | 'users' => [ 26 | 'type' => 'EmbedsMany', 27 | 'mode' => 'classic', 28 | 'model' => 'Tests\Models\MiniUser', 29 | 'modelTarget' => 'Tests\Models\User', 30 | 'methodOnTarget' => 'roles', 31 | 'modelOnTarget' => 'Tests\Models\MiniRole', 32 | ], 33 | ]; 34 | 35 | public function permissions() 36 | { 37 | return $this->embedsMany('Tests\Models\MiniPermission'); 38 | } 39 | 40 | public function users() 41 | { 42 | return $this->embedsMany('Tests\Models\MiniUser'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Models/SubItem.php: -------------------------------------------------------------------------------- 1 | [ 21 | 'is-ml' => true, 22 | ], 23 | 'code' => [], 24 | 'href' => [], 25 | ]; 26 | 27 | protected $mongoRelation = [ 28 | 'navigation' => [ 29 | 'type' => 'EmbedsOne', 30 | 'mode' => 'classic', 31 | 'model' => 'Tests\Models\MiniNavigation', 32 | 'modelTarget' => 'Tests\Models\Navigation', 33 | 'methodOnTarget' => 'sub_items', 34 | 'modelOnTarget' => 'Tests\Models\MiniSubItem', 35 | 'typeOnTarget' => 'EmbedsMany', 36 | ], 37 | ]; 38 | 39 | public function navigation() 40 | { 41 | return $this->embedsOne('Tests\Models\MiniNavigation'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Models/User.php: -------------------------------------------------------------------------------- 1 | [], 11 | 'name' => [], 12 | 'surname' => [], 13 | ]; 14 | 15 | protected $mongoRelation = [ 16 | 'permissions' => [ 17 | 'type' => 'EmbedsMany', 18 | 'mode' => 'classic', 19 | 'model' => 'Tests\Models\MiniPermission', 20 | 'modelTarget' => 'Tests\Models\Permission', 21 | 'methodOnTarget' => 'users', 22 | 'modelOnTarget' => 'Tests\Models\MiniUser', 23 | ], 24 | 'roles' => [ 25 | 'type' => 'EmbedsMany', 26 | 'mode' => 'classic', 27 | 'model' => 'Tests\Models\MiniRole', 28 | 'modelTarget' => 'Tests\Models\Role', 29 | 'methodOnTarget' => 'users', 30 | 'modelOnTarget' => 'Tests\Models\MiniUser', 31 | ], 32 | ]; 33 | 34 | public function roles() 35 | { 36 | return $this->embedsMany('Tests\Models\MiniRole'); 37 | } 38 | 39 | public function permissions() 40 | { 41 | return $this->embedsMany('Tests\Models\MiniPermission'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/MongoCollectionTest.php: -------------------------------------------------------------------------------- 1 | cleanDb(); 18 | 19 | $this->prepareArticleData(['status' => 'published'], 15); 20 | 21 | $this->prepareArticleData(['status' => 'draft'], 5); 22 | 23 | //Not found by slug 404 24 | 25 | try { 26 | Article::all()->getBySlugAndStatus('sport', 'articolo'); 27 | } catch (Throwable $e) { 28 | $this->assertEquals( 29 | new NotFoundHttpException('error.Tests\Models\Article'), 30 | $e 31 | ); 32 | } 33 | 34 | //Not found by all parameters null 404 35 | try { 36 | Article::all()->getBySlugAndStatus(null, null); 37 | } catch (Throwable $e) { 38 | $this->assertEquals( 39 | new NotFoundHttpException('error.Tests\Models\Article'), 40 | $e 41 | ); 42 | } 43 | 44 | //Not found by category null 404 45 | try { 46 | Article::all()->getBySlugAndStatus(null, 'articolo-1'); 47 | } catch (Throwable $e) { 48 | $this->assertEquals( 49 | new NotFoundHttpException('error.Tests\Models\Article'), 50 | $e 51 | ); 52 | } 53 | 54 | //Not found by title null 404 55 | try { 56 | Article::all()->getBySlugAndStatus('sport', null); 57 | } catch (Throwable $e) { 58 | $this->assertEquals( 59 | new NotFoundHttpException('error.Tests\Models\Article'), 60 | $e 61 | ); 62 | } 63 | 64 | //Not found by category not existing 404 65 | try { 66 | Article::all()->getBySlugAndStatus('category-not-existing', 'articolo-1'); 67 | } catch (Throwable $e) { 68 | $this->assertEquals( 69 | new NotFoundHttpException('error.Tests\Models\Article'), 70 | $e 71 | ); 72 | } 73 | 74 | //Not found draft article not existing 404 75 | try { 76 | Article::all()->getBySlugAndStatus('sport', 'articolo-16'); 77 | } catch (Throwable $e) { 78 | $this->assertEquals( 79 | new NotFoundHttpException('error.Tests\Models\Article'), 80 | $e 81 | ); 82 | } 83 | 84 | //Check if instance of Article is passed 85 | $outPublished = Article::all()->getBySlugAndStatus('sport', 'articolo-1'); 86 | $this->assertInstanceOf(Article::class, $outPublished); 87 | $this->assertEquals('articolo 1', getTranslatedContent($outPublished->title)); 88 | $this->assertEquals('articolo-1', getTranslatedContent($outPublished->slug)); 89 | $this->assertEquals('sport', getTranslatedContent($outPublished->primarycategory->name)); 90 | 91 | $this->cleanDb(); 92 | } 93 | 94 | public function test_getBySlug() 95 | { 96 | $this->cleanDb(); 97 | 98 | $article = $this->prepareArticleData([], 5); 99 | 100 | $this->expectException(NotFoundHttpException::class); 101 | 102 | $out = Article::all()->getBySlug('articolo-1'); 103 | $this->assertInstanceOf(Article::class, $out); 104 | 105 | $outNotFoundBySlug = Article::all()->getBySlugAndStatus('sport', 'articolo'); 106 | $outNotFoundByCategory = Article::all()->getBySlugAndStatus('sports', 'articolo-1'); 107 | 108 | $this->cleanDb(); 109 | } 110 | 111 | public function test_getNotDeleted() 112 | { 113 | $this->cleanDb(); 114 | 115 | $this->prepareArticleData(['is_deleted' => true], 5); 116 | $this->prepareArticleData(['is_deleted' => false], 3); 117 | 118 | $allArticles = Article::all(); 119 | $notDeletedArticles = $allArticles->getNotDeleted(); 120 | 121 | $this->assertCount(3, $notDeletedArticles); 122 | $this->assertCount(8, $allArticles); 123 | 124 | $this->cleanDb(); 125 | } 126 | 127 | public function test_getPublished() 128 | { 129 | $this->cleanDb(); 130 | 131 | $this->prepareArticleData(['status' => 'published'], 5); 132 | $this->prepareArticleData(['status' => 'draft'], 3); 133 | 134 | $allArticles = Article::all(); 135 | $getPublished = $allArticles->getPublished(); 136 | 137 | $this->assertCount(5, $getPublished); 138 | $this->assertCount(8, $allArticles); 139 | 140 | $this->cleanDb(); 141 | } 142 | 143 | public function test_getPublic() 144 | { 145 | $this->cleanDb(); 146 | 147 | $this->prepareArticleData(['visibility' => 'public'], 5); 148 | $this->prepareArticleData(['visibility' => 'hidden'], 3); 149 | 150 | $allArticles = Article::all(); 151 | $public = $allArticles->getPublic(); 152 | 153 | $this->assertCount(5, $public); 154 | $this->assertCount(8, $allArticles); 155 | 156 | $this->cleanDb(); 157 | } 158 | 159 | public function test_hasItem() 160 | { 161 | $this->cleanDb(); 162 | 163 | $this->prepareArticleData([], 1); 164 | $this->createCategory(['name' => 'news']); 165 | 166 | $idNull = $this->getIdNull(); 167 | $article = Article::all()->first(); 168 | $categoryAssigned = Category::where('name.'.cl(), 'sport')->first(); 169 | $categoryNotAssigned = Category::where('name.'.cl(), 'news')->first(); 170 | $out = $article->categories->hasItem(null); 171 | $this->assertFalse($out); 172 | 173 | $out = $article->categories->hasItem($categoryAssigned); 174 | $this->assertTrue($out); 175 | 176 | $out = $article->categories->hasItem($categoryNotAssigned); 177 | $this->assertFalse($out); 178 | 179 | $out = $article->categories->hasItem($idNull); 180 | $this->assertFalse($out); 181 | 182 | $this->cleanDb(); 183 | } 184 | 185 | public function test_moveFirst() 186 | { 187 | $this->cleanDb(); 188 | 189 | $this->prepareArticleDataWithTwoCategories(); 190 | 191 | $article = Article::all()->first(); 192 | $out = $article->categories->moveFirst($article->primarycategory->ref_id); 193 | 194 | $this->assertEquals('news', getTranslatedContent($out->first()->name)); 195 | $this->assertCount(2, $out); 196 | 197 | $this->cleanDb(); 198 | } 199 | 200 | public function test_getActive() 201 | { 202 | $this->cleanDb(); 203 | 204 | $this->prepareArticleData(['is_active' => true], 2); 205 | $this->prepareArticleData(['is_active' => false]); 206 | 207 | $allArticles = Article::all(); 208 | $active = $allArticles->getActive(); 209 | $notActiveCount = $allArticles->count() - $active->count(); 210 | 211 | $this->assertCount(2, $active); 212 | $this->assertCount(3, $allArticles); 213 | $this->assertEquals(1, $notActiveCount); 214 | 215 | $this->cleanDb(); 216 | } 217 | 218 | public function test_exist() 219 | { 220 | $this->cleanDb(); 221 | 222 | //Test not Exist - return value false 223 | $allArticles = Article::all(); 224 | $out = $allArticles->exist(); 225 | 226 | $this->assertFalse($out, $out); 227 | $this->assertCount(0, $allArticles); 228 | 229 | //Test Exist - return value true 230 | $this->prepareArticleData([], 2); 231 | 232 | $allArticles = Article::all(); 233 | $out = $allArticles->exist(); 234 | 235 | $this->assertEquals($out, $out); 236 | $this->assertCount(2, $allArticles); 237 | 238 | $this->cleanDb(); 239 | } 240 | 241 | public function test_findByAID() 242 | { 243 | $this->cleanDb(); 244 | $this->prepareArticleData(['title' => 'My autoincrement title']); 245 | $this->prepareArticleData([], 2); 246 | 247 | $allArticles = Article::all(); 248 | $out = $allArticles->findByAID(1); 249 | 250 | $this->assertEquals('My autoincrement title', getTranslatedContent($out->title)); 251 | $this->assertCount(3, $allArticles); 252 | $this->cleanDb(); 253 | 254 | $this->cleanDb(); 255 | $this->prepareArticleData([], 2); 256 | $this->prepareArticleData(['title' => 'My autoincrement title']); 257 | 258 | $allArticles = Article::all(); 259 | $out = $allArticles->findByAID(3); 260 | 261 | $this->assertEquals('My autoincrement title', getTranslatedContent($out->title)); 262 | $this->assertCount(3, $allArticles); 263 | $this->cleanDb(); 264 | } 265 | 266 | public function test_hasPermission() 267 | { 268 | $this->cleanDbUPR(); 269 | 270 | $this->prepareUserData(); 271 | 272 | $permissionNotExisting = '111'; 273 | 274 | $out = User::all()->first()->permissions->hasPermission($permissionNotExisting); 275 | 276 | $this->assertFalse($out); 277 | 278 | $permissionExisting = Permission::where('name', 'EditArticle')->first(); 279 | $out = User::all()->first()->permissions->hasPermission($permissionExisting->id); 280 | 281 | $this->assertTrue($out); 282 | 283 | $out = User::all()->first()->permissions->hasPermission(null); 284 | 285 | $this->assertFalse($out); 286 | 287 | $this->cleanDbUPR(); 288 | } 289 | 290 | public function test_hasRole() 291 | { 292 | $this->cleanDbUPR(); 293 | 294 | $this->prepareUserData(); 295 | 296 | $out = User::all()->first()->roles->hasRole('RoleNotExisting'); 297 | 298 | $this->assertFalse($out); 299 | 300 | $out = User::all()->first()->roles->hasRole('SuperAdmin'); 301 | 302 | $this->assertTrue($out); 303 | 304 | $out = User::all()->first()->roles->hasRole(null); 305 | 306 | $this->assertFalse($out); 307 | 308 | $this->cleanDbUPR(); 309 | } 310 | 311 | public function test_checkPermission() 312 | { 313 | $this->cleanDbUPR(); 314 | 315 | $this->prepareUserData(); 316 | 317 | $permissionNotExisting = 'CreateArticle'; 318 | 319 | $out = User::all()->first()->permissions->checkPermission($permissionNotExisting); 320 | 321 | $this->assertFalse($out); 322 | 323 | $permissionExisting = 'EditArticle'; 324 | $out = User::all()->first()->permissions->checkPermission($permissionExisting); 325 | 326 | $this->assertTrue($out); 327 | 328 | $out = User::all()->first()->permissions->checkPermission(null); 329 | 330 | $this->assertFalse($out); 331 | 332 | $this->cleanDbUPR(); 333 | } 334 | 335 | private function cleanDb() 336 | { 337 | Category::truncate(); 338 | Article::truncate(); 339 | } 340 | 341 | private function cleanDbUPR() 342 | { 343 | User::truncate(); 344 | Permission::truncate(); 345 | Role::truncate(); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /tests/MongoSyncTraitTest.php: -------------------------------------------------------------------------------- 1 | createNavigation(); 19 | 20 | $arr = [ 21 | 'text' => null, 22 | ]; 23 | $options = [ 24 | 'request_type' => 'partial', 25 | ]; 26 | 27 | $navigation->updateWithSync($request, $arr, $options); 28 | 29 | $this->assertNull($navigation->text); 30 | } 31 | 32 | public function test_store_with_sync() 33 | { 34 | $request = new Request; 35 | 36 | $faker = Factory::create(); 37 | 38 | $navigation = new Navigation; 39 | 40 | $arr = [ 41 | 'text' => $faker->text(50), 42 | 'code' => $faker->creditCardNumber, 43 | 'href' => $faker->url, 44 | 'title' => $faker->text(30), 45 | 'date' => new UTCDateTime(new DateTime()), 46 | 'target' => (object) [ 47 | 'name' => $faker->text(20), 48 | 'code' => $faker->slug(2), 49 | ], 50 | ]; 51 | 52 | $navigation->storeWithSync($request, $arr); 53 | 54 | $this->assertTrue($this->isNavigationCreated($navigation)); 55 | } 56 | 57 | public function test_store_different_input_type() 58 | { 59 | $request = new Request; 60 | 61 | $faker = Factory::create(); 62 | 63 | $navigation = new Navigation; 64 | 65 | $arr = [ 66 | 'text' => $faker->text(50), 67 | 'code' => $faker->creditCardNumber, 68 | 'href' => null, 69 | 'title' => $faker->text(30), 70 | 'date' => new UTCDateTime(new DateTime()), 71 | 'target' => (object) [], 72 | ]; 73 | 74 | $navigation->storeWithSync($request, $arr); 75 | 76 | $this->assertTrue($this->isNavigationCreated($navigation)); 77 | $this->assertIsString($navigation->text); 78 | $this->assertNull($navigation->href); 79 | $this->assertIsArray($navigation->title); 80 | $this->assertIsObject($navigation->target); 81 | } 82 | 83 | public function test_update_with_sync_with_embeds_one_on_target() 84 | { 85 | $request = new Request; 86 | 87 | $faker = Factory::create(); 88 | 89 | $navigation = Navigation::all()->last(); 90 | 91 | $options = [ 92 | 'request_type' => 'partial', 93 | ]; 94 | 95 | $arr = [ 96 | 'text' => 'Aggiornato', 97 | 'code' => $faker->creditCardNumber, 98 | 'href' => $faker->imageUrl(), 99 | 'title' => $faker->text(30), 100 | 'date' => null, 101 | 'target' => (object) [], 102 | ]; 103 | 104 | $navigation->updateWithSync($request, $arr, $options); 105 | 106 | $this->assertTrue($this->isUpdated($navigation)); 107 | } 108 | 109 | public function test_update_with_sync_with_embeds_many_on_target() 110 | { 111 | $request = new Request; 112 | 113 | $faker = Factory::create(); 114 | 115 | $navigation = Navigation::all()->last(); 116 | 117 | $options = [ 118 | 'request_type' => 'partial', 119 | ]; 120 | 121 | $arr = [ 122 | 'text' => 'Aggiornato', 123 | 'code' => $faker->creditCardNumber, 124 | 'href' => $faker->imageUrl(), 125 | 'title' => $faker->text(30), 126 | 'date' => null, 127 | 'target' => (object) [], 128 | ]; 129 | 130 | $navigation->updateWithSync($request, $arr, $options); 131 | 132 | $this->assertTrue($this->isUpdated($navigation)); 133 | } 134 | 135 | public function test_update_different_input_type() 136 | { 137 | $request = new Request; 138 | 139 | $faker = Factory::create(); 140 | 141 | $navigation = Navigation::all()->last(); 142 | 143 | $options = [ 144 | 'request_type' => 'partial', 145 | ]; 146 | 147 | $arr = [ 148 | 'text' => 'Aggiornato', 149 | 'code' => $faker->creditCardNumber, 150 | 'href' => $faker->text(50), 151 | 'title' => $faker->text(30), 152 | 'date' => new UTCDateTime(new DateTime()), 153 | 'target' => (object) [], 154 | ]; 155 | 156 | $navigation->updateWithSync($request, $arr, $options); 157 | 158 | $this->assertTrue($this->isUpdated($navigation)); 159 | $this->assertIsObject($navigation->target); 160 | $this->assertIsArray($navigation->title); 161 | $this->assertIsString($navigation->href); 162 | } 163 | 164 | public function test_update_null_value_on_all_field_except_text_and_code() 165 | { 166 | $request = new Request; 167 | 168 | $faker = Factory::create(); 169 | 170 | $navigation = Navigation::all()->last(); 171 | 172 | $options = [ 173 | 'request_type' => 'partial', 174 | ]; 175 | 176 | $arr = [ 177 | 'text' => 'Aggiornato', 178 | 'code' => $faker->creditCardNumber, 179 | 'href' => null, 180 | 'title' => null, 181 | 'date' => null, 182 | 'target' => null, 183 | ]; 184 | 185 | $navigation->updateWithSync($request, $arr, $options); 186 | 187 | $this->assertTrue($this->isUpdated($navigation)); 188 | /*$this->assertNull($navigation->href); 189 | $this->assertNull($navigation->title[cl()]); 190 | $this->assertNull($navigation->date); 191 | $this->assertNull($navigation->target);*/ 192 | } 193 | 194 | public function test_store_item_with_relation() 195 | { 196 | $request = new Request; 197 | $navigation = $this->createNavigation(); 198 | $faker = Factory::create(); 199 | 200 | $item = new Item; 201 | 202 | $arr = [ 203 | 'name' => $faker->firstName.' '.$faker->lastName, 204 | 'code' => $faker->creditCardNumber, 205 | 'price' => $faker->numberBetween(1, 100), 206 | 'quantity' => $faker->numberBetween(1, 10), 207 | 'discount' => $faker->randomElement([10, 20, 50]), 208 | 'taxable_price' => $faker->numberBetween(10, 500), 209 | 'partial_vat' => $faker->numberBetween(20, 50), 210 | 'total_price' => $faker->numberBetween(20, 600), 211 | 'vat_code' => $faker->randomElement([ 212 | '0', 213 | '3', 214 | '4', 215 | ]), 216 | 'vat_value' => $faker->randomElement([ 217 | 22, 218 | 10, 219 | 4, 220 | ]), 221 | 'vat_label' => $faker->randomElement([ 222 | 'Iva 22%', 223 | 'Iva 10%', 224 | 'Iva 4%', 225 | ]), 226 | 'collection_type' => null, 227 | 'navigation_code' => $navigation->code, 228 | 229 | //Relation 230 | 'navigation' => $this->getNavigation($navigation->code), 231 | ]; 232 | 233 | $item = $item->storeWithSync($request, $arr); 234 | $mini_items = Navigation::where('sub_items.ref_id', $item->getId())->first()->sub_items; 235 | 236 | $this->assertTrue($this->isItemCreated($item)); 237 | $this->assertNotNull($mini_items); 238 | } 239 | 240 | public function test_update_navigation_with_items() 241 | { 242 | $navigation = $this->createNavigation(); 243 | 244 | $request = new Request; 245 | 246 | $options = [ 247 | 'request_type' => 'partial', 248 | ]; 249 | 250 | $arr = [ 251 | 'text' => $navigation->text.' Aggiornato', 252 | 'code' => $navigation->code, 253 | 'href' => $navigation->href, 254 | 'title' => $navigation->title[cl()], 255 | 'date' => $navigation->date, 256 | 'target' => $navigation->target, 257 | ]; 258 | 259 | $navigation->updateWithSync($request, $arr, $options); 260 | 261 | $this->assertTrue($this->isNavigationUpdatedCorrectly($navigation)); 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /tests/SimpleTestCase.php: -------------------------------------------------------------------------------- 1 | createSubItems( 19 | [ 20 | 'text' => 'example sub item test', 21 | 'code' => 'HFGRT12345', 22 | 'href' => 'https://google.com', 23 | ] 24 | ); 25 | 26 | $this->assertEquals('example sub item test', getTranslatedContent($sub_item->text)); 27 | $this->assertEquals('HFGRT12345', $sub_item->code); 28 | $this->assertEquals('https://google.com', $sub_item->href); 29 | 30 | //Create a mini Sub Item to associate to the new navigation 31 | $sub_items = json_encode( 32 | [ 33 | (object) [ 34 | 'ref_id' => $sub_item->id, 35 | 'text' => getTranslatedContent($sub_item->text), 36 | 'code' => $sub_item->code, 37 | 'href' => $sub_item->href, 38 | ], 39 | ] 40 | ); 41 | 42 | $date = Date::now(); 43 | 44 | //Create a navigation and test if the data is store correctly 45 | $navigation = $this->createNavigation( 46 | [ 47 | 'text' => 'example navigation text', 48 | 'code' => '1234ABHFGRT5', 49 | 'href' => 'https://www.netflix.com/browse', 50 | 'date' => $date, 51 | 'target' => '_blank', 52 | 'title' => 'Random title', 53 | 'sub_items' => $sub_items, 54 | ] 55 | ); 56 | 57 | $this->assertTrue($this->isNavigationCreated($navigation)); 58 | $this->assertIsString($navigation->text); 59 | $this->assertIsArray($navigation->title); 60 | 61 | $this->assertEquals('example navigation text', $navigation->text); 62 | $this->assertEquals('1234ABHFGRT5', $navigation->code); 63 | $this->assertEquals('https://www.netflix.com/browse', $navigation->href); 64 | //$this->assertEquals($date, $navigation->date); TODO: fix precision date 65 | $this->assertEquals('_blank', $navigation->target); 66 | $this->assertEquals('Random title', getTranslatedContent($navigation->title)); 67 | $this->assertInstanceOf(MongoCollection::class, $navigation->sub_items); 68 | 69 | //Check if the subitem target is updated correctly 70 | $sub_item = SubItem::find($sub_item->id); 71 | $mini_navigation = $sub_item->navigation; 72 | $this->assertNotNull($mini_navigation); 73 | $this->assertEquals($navigation->id, $mini_navigation->ref_id); 74 | $this->assertEquals('1234ABHFGRT5', $mini_navigation->code); 75 | $this->assertEquals('Random title', getTranslatedContent($mini_navigation->title)); 76 | $this->assertEquals('example navigation text', $navigation->text); 77 | 78 | //Clean all data that has been stored 79 | $this->cleanUp($navigation, $sub_item); 80 | } 81 | 82 | public function test_store_with_embeds_many_on_target() 83 | { 84 | //Create a navigation and test if the data is store correctly 85 | $navigation = $this->createNavigation(); 86 | 87 | $this->assertTrue($this->isNavigationCreated($navigation)); 88 | $this->assertInstanceOf(MongoCollection::class, $navigation->sub_items); 89 | $mini_navigation = $this->getMiniNavigation($navigation->id); 90 | 91 | $sub_item = $this->createSubItems( 92 | [ 93 | 'text' => 'example sub item test', 94 | 'code' => 'HFGRT12345', 95 | 'href' => 'https://google.com', 96 | 'navigation' => $mini_navigation, 97 | ] 98 | ); 99 | 100 | $this->assertEquals('example sub item test', getTranslatedContent($sub_item->text)); 101 | $this->assertEquals('HFGRT12345', $sub_item->code); 102 | $this->assertEquals('https://google.com', $sub_item->href); 103 | 104 | //Check target 105 | $navigation = Navigation::find($navigation->id); 106 | 107 | $sub_item_mini = $navigation->sub_items[0]; 108 | 109 | $this->assertNotEmpty($navigation->sub_items); 110 | $this->assertEquals($sub_item->id, $sub_item_mini->ref_id); 111 | $this->assertEquals(getTranslatedContent($sub_item->text), getTranslatedContent($sub_item_mini->text)); 112 | $this->assertEquals($sub_item->code, $sub_item_mini->code); 113 | $this->assertEquals($sub_item->href, $sub_item_mini->href); 114 | 115 | $faker = Factory::create(); 116 | //Add more sub items and restart test 117 | $navigation->sub_items = [ 118 | [ 119 | 'ref_id' => $faker->uuid, 120 | 'text' => $faker->text, 121 | 'code' => $faker->name, 122 | 'href' => $faker->url, 123 | ], 124 | [ 125 | 'ref_id' => $faker->uuid, 126 | 'text' => $faker->text, 127 | 'code' => $faker->name, 128 | 'href' => $faker->url, 129 | ], 130 | [ 131 | 'ref_id' => $faker->uuid, 132 | 'text' => $faker->text, 133 | 'code' => $faker->name, 134 | 'href' => $faker->url, 135 | ], 136 | ]; 137 | 138 | $navigation->save(); 139 | 140 | $mini_navigation = $this->getMiniNavigation($navigation->id); 141 | $data = [ 142 | 'navigation' => $mini_navigation, 143 | ]; 144 | 145 | $sub_item = $this->createSubItems($data); 146 | $navigation = Navigation::find($navigation->id); 147 | 148 | $this->assertEquals(4, $navigation->sub_items->count()); 149 | 150 | $sub_item_mini = $navigation->sub_items->where('ref_id', $sub_item->id)->first(); 151 | 152 | $this->assertEquals($sub_item->id, $sub_item_mini->ref_id); 153 | $this->assertEquals($sub_item->text, $sub_item_mini->text); 154 | $this->assertEquals($sub_item->code, $sub_item_mini->code); 155 | $this->assertEquals($sub_item->title, $sub_item_mini->title); 156 | 157 | //clean data 158 | $navigation->delete(); 159 | $sub_item->delete(); 160 | } 161 | 162 | public function test_store_with_embeds_one_on_target_just_filled() 163 | { 164 | $faker = Factory::create(); 165 | 166 | $navigation = new Navigation; 167 | $navigation = $navigation->storeWithSync(new Request, [ 168 | 'text' => $faker->text(50), 169 | 'code' => $faker->creditCardNumber, 170 | 'href' => $faker->url, 171 | 'date' => Date::now(), 172 | 'target' => $faker->text(50), 173 | 'title' => null, 174 | 'sub_items' => json_encode([]), 175 | ]); 176 | 177 | $this->assertTrue($this->isNavigationCreated($navigation)); 178 | $this->assertInstanceOf(MongoCollection::class, $navigation->sub_items); 179 | 180 | // 1 navigation 181 | 182 | $first_sub_item = $this->createSubItems([ 183 | 'code' => 'HFGRT12345', 184 | 'navigation' => $this->getMiniNavigation($navigation->id), 185 | ]); 186 | 187 | $this->assertEquals('HFGRT12345', $first_sub_item->code); 188 | $this->assertEquals($navigation->id, $first_sub_item->navigation->ref_id); 189 | $this->assertInstanceOf(MiniNavigation::class, $first_sub_item->navigation); 190 | 191 | // 1 navigation with 1 sub item and 1 sub item with navigation 192 | 193 | $second_sub_item = $this->createSubItems([ 194 | 'code' => 'HFGRT12346', 195 | 'navigation' => $this->getMiniNavigation($navigation->id), 196 | ]); 197 | 198 | $this->assertEquals('HFGRT12346', $second_sub_item->code); 199 | $this->assertEquals($navigation->id, $second_sub_item->navigation->ref_id); 200 | $this->assertInstanceOf(MiniNavigation::class, $second_sub_item->navigation); 201 | 202 | // 1 navigation with 2 sub items and 1 sub item with navigation 203 | 204 | $navigation = Navigation::find($navigation->id); 205 | 206 | $this->assertCount(2, $navigation->sub_items); 207 | $this->assertEquals('HFGRT12345', $navigation->sub_items[0]->code); 208 | $this->assertEquals('HFGRT12346', $navigation->sub_items[1]->code); 209 | 210 | $navigation->delete(); 211 | $first_sub_item->delete(); 212 | $second_sub_item->delete(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /tests/SyncTestCase.php: -------------------------------------------------------------------------------- 1 | text(50); 36 | $code = Arr::has($data, 'code') ? Arr::get($data, 'code') : $faker->creditCardNumber; 37 | $href = Arr::has($data, 'href') ? Arr::get($data, 'href') : $faker->url; 38 | $navigation = Arr::has($data, 'navigation') ? Arr::get($data, 'navigation') : json_encode([]); 39 | 40 | $arr = [ 41 | 'text' => $text, 42 | 'code' => $code, 43 | 'href' => $href, 44 | 'navigation' => $navigation, 45 | ]; 46 | 47 | return $sub_item->storeWithSync($request, $arr); 48 | } 49 | 50 | /** 51 | * @param array $data 52 | * @return Item 53 | * 54 | * @throws Exception 55 | */ 56 | public function createItems(array $data = []) 57 | { 58 | $sub_item = new Item; 59 | $faker = Factory::create(); 60 | $request = new Request; 61 | 62 | $text = Arr::has($data, 'text') ? Arr::get($data, 'text') : $faker->text(50); 63 | $code = Arr::has($data, 'code') ? Arr::get($data, 'code') : $faker->creditCardNumber; 64 | $href = Arr::has($data, 'href') ? Arr::get($data, 'href') : $faker->url; 65 | $navigation = Arr::has($data, 'navigation') ? Arr::get($data, 'navigation') : json_encode([]); 66 | 67 | $arr = [ 68 | 'text' => $text, 69 | 'name' => $text, 70 | 'code' => $code, 71 | 'href' => $href, 72 | 'navigation' => $navigation, 73 | ]; 74 | 75 | return $sub_item->storeWithSync($request, $arr); 76 | } 77 | 78 | /** 79 | * @param array $data 80 | * @return Navigation 81 | * 82 | * @throws Exception 83 | */ 84 | public function createNavigation( 85 | array $data = [] 86 | ) { 87 | $faker = Factory::create(); 88 | $request = new Request; 89 | 90 | $navigation = new Navigation; 91 | 92 | $text = Arr::has($data, 'text') ? Arr::get($data, 'text') : $faker->text(50); 93 | $code = Arr::has($data, 'code') ? Arr::get($data, 'code') : $faker->creditCardNumber; 94 | $href = Arr::has($data, 'href') ? Arr::get($data, 'href') : $faker->url; 95 | $date = Arr::has($data, 'date') ? Arr::get($data, 'date') : Date::now(); 96 | $target = Arr::has($data, 'target') ? Arr::get($data, 'target') : $faker->text(50); 97 | $title = Arr::has($data, 'title') ? Arr::get($data, 'title') : $faker->title; 98 | 99 | $sub_items = Arr::has($data, 'sub_items') ? Arr::get($data, 'sub_items') : json_encode([]); 100 | 101 | $arr = [ 102 | 'text' => $text, 103 | 'code' => $code, 104 | 'href' => $href, 105 | 'date' => $date, 106 | 'target' => $target, 107 | 'title' => $title, 108 | 'sub_items' => $sub_items, 109 | ]; 110 | 111 | return $navigation->storeWithSync($request, $arr); 112 | } 113 | 114 | /** 115 | * @param array $data 116 | * @return Category 117 | * 118 | * @throws Exception 119 | */ 120 | public function createCategory( 121 | array $data = [] 122 | ) { 123 | $faker = Factory::create(); 124 | $request = new Request; 125 | 126 | $category = new Category; 127 | 128 | $category_id = getAID($category); 129 | $name = Arr::has($data, 'name') ? Arr::get($data, 'name') : $faker->text(50); 130 | $slug = Arr::has($data, 'slug') ? Arr::get($data, 'slug') : Str::slug($name); 131 | $description = Arr::has($data, 'description') ? Arr::get($data, 'description') : $faker->text(50); 132 | 133 | $articles = Arr::has($data, 'articles') ? Arr::get($data, 'articles') : json_encode([]); 134 | 135 | $arr = [ 136 | 'category_id' => $category_id, 137 | 'name' => $name, 138 | 'slug' => $slug, 139 | 'description' => $description, 140 | 'articles' => $articles, 141 | ]; 142 | 143 | return $category->storeWithSync($request, $arr); 144 | } 145 | 146 | /** 147 | * @param array $data 148 | * @return Permission 149 | * 150 | * @throws Exception 151 | */ 152 | public function createPermission( 153 | array $data = [] 154 | ) { 155 | $faker = Factory::create(); 156 | $request = new Request; 157 | 158 | $permission = new Permission; 159 | 160 | $name = Arr::has($data, 'name') ? Arr::get($data, 'name') : $faker->name; 161 | $label = Arr::has($data, 'label') ? Arr::get($data, 'label') : $faker->name; 162 | 163 | $roles = Arr::has($data, 'roles') ? Arr::get($data, 'roles') : json_encode([]); 164 | $users = Arr::has($data, 'users') ? Arr::get($data, 'users') : json_encode([]); 165 | 166 | $arr = [ 167 | 'name' => $name, 168 | 'label' => $label, 169 | 'roles' => $roles, 170 | 'users' => $users, 171 | ]; 172 | 173 | return $permission->storeWithSync($request, $arr); 174 | } 175 | 176 | /** 177 | * @param array $data 178 | * @return Role 179 | * 180 | * @throws Exception 181 | */ 182 | public function createRole( 183 | array $data = [] 184 | ) { 185 | $faker = Factory::create(); 186 | $request = new Request; 187 | 188 | $role = new Role; 189 | 190 | $name = Arr::has($data, 'name') ? Arr::get($data, 'name') : $faker->name; 191 | $label = Arr::has($data, 'label') ? Arr::get($data, 'label') : $faker->name; 192 | 193 | $permissions = Arr::has($data, 'permissions') ? Arr::get($data, 'permissions') : json_encode([]); 194 | $users = Arr::has($data, 'users') ? Arr::get($data, 'users') : json_encode([]); 195 | 196 | $arr = [ 197 | 'name' => $name, 198 | 'label' => $label, 199 | 'permissions' => $permissions, 200 | 'users' => $users, 201 | ]; 202 | 203 | return $role->storeWithSync($request, $arr); 204 | } 205 | 206 | /** 207 | * @param array $data 208 | * @param int $size 209 | * 210 | * @throws Exception 211 | */ 212 | public function createArticles( 213 | array $data = [], 214 | int $size = 1 215 | ) { 216 | $faker = Factory::create(); 217 | 218 | for ($i = 0; $i < $size; $i++) { 219 | $request = new Request; 220 | $article = new Article; 221 | 222 | $autoincrement_id = getAID($article); 223 | $title = Arr::has($data, 'title') ? Arr::get($data, 'title') : 'articolo '.$autoincrement_id; 224 | $content = Arr::has($data, 'content') ? Arr::get($data, 'name') : $faker->text(100); 225 | $slug = Arr::has($data, 'slug') ? Arr::get($data, 'slug') : Str::slug($title); 226 | $visibility = Arr::has($data, 'visibility') ? Arr::get($data, 'visibility') : $faker->text(50); 227 | $status = Arr::has($data, 'status') ? Arr::get($data, 'status') : $faker->text(50); 228 | $is_deleted = Arr::has($data, 'is_deleted') ? Arr::get($data, 'is_deleted') : $faker->boolean; 229 | $is_active = Arr::has($data, 'is_active') ? Arr::get($data, 'is_active') : $faker->text(50); 230 | $primarycategory = Arr::has($data, 'primarycategory') ? Arr::get($data, 'primarycategory') : $faker->text(50); 231 | 232 | $categories = Arr::has($data, 'categories') ? Arr::get($data, 'categories') : json_encode([]); 233 | 234 | $arr = [ 235 | 'autoincrement_id' => $autoincrement_id, 236 | 'title' => $title, 237 | 'content' => $content, 238 | 'slug' => $slug, 239 | 'visibility' => $visibility, 240 | 'status' => $status, 241 | 'is_deleted' => $is_deleted, 242 | 'is_active' => $is_active, 243 | 'primarycategory' => $primarycategory, 244 | 'categories' => $categories, 245 | ]; 246 | 247 | $article->storeWithSync($request, $arr); 248 | } 249 | } 250 | 251 | /** 252 | * @param array $data 253 | * @param int $size 254 | * 255 | * @throws Exception 256 | */ 257 | public function createUsers( 258 | array $data = [], 259 | int $size = 1 260 | ) { 261 | $faker = Factory::create(); 262 | 263 | for ($i = 0; $i < $size; $i++) { 264 | $request = new Request; 265 | $user = new User; 266 | 267 | $name = Arr::has($data, 'name') ? Arr::get($data, 'name') : $faker->firstName; 268 | $surname = Arr::has($data, 'surname') ? Arr::get($data, 'surname') : $faker->lastName; 269 | $email = Arr::has($data, 'email') ? Arr::get($data, 'email') : $faker->email; 270 | 271 | $roles = Arr::has($data, 'roles') ? Arr::get($data, 'roles') : json_encode([]); 272 | $permissions = Arr::has($data, 'permissions') ? Arr::get($data, 'permissions') : json_encode([]); 273 | 274 | $arr = [ 275 | 'name' => $name, 276 | 'surname' => $surname, 277 | 'email' => $email, 278 | 'roles' => $roles, 279 | 'permissions' => $permissions, 280 | ]; 281 | $user->storeWithSync($request, $arr); 282 | } 283 | } 284 | 285 | /** 286 | * @param array $data 287 | * @param int $size 288 | * 289 | * @throws Exception 290 | */ 291 | public function prepareArticleData( 292 | array $data = [], 293 | int $size = 1 294 | ) { 295 | $category = $this->createCategory(['name' => 'sport']); 296 | $miniCategory = $this->getMiniCategory($category->id); 297 | 298 | $relationshipValues = [ 299 | 'primarycategory' => $miniCategory, 300 | 'categories' => $miniCategory, 301 | ]; 302 | 303 | $mergedData = array_merge($relationshipValues, $data); 304 | 305 | $this->createArticles($mergedData, $size); 306 | } 307 | 308 | public function prepareUserData( 309 | array $data = [], 310 | int $size = 1 311 | ) { 312 | $first_permission = $this->createPermission(['name' => 'EditArticle']); 313 | $second_permission = $this->createPermission(['name' => 'CreateUser']); 314 | 315 | $first_role = $this->createRole(['name' => 'SuperAdmin']); 316 | $second_role = $this->createRole(['name' => 'Editor']); 317 | 318 | $miniPermissions = $this->getMiniPermissions([ 319 | $first_permission->id, 320 | $second_permission->id, 321 | ]); 322 | 323 | $miniRoles = $this->getMiniRoles([ 324 | $first_role->id, 325 | $second_role->id, ] 326 | ); 327 | 328 | $relationshipValues = [ 329 | 'permissions' => $miniPermissions, 330 | 'roles' => $miniRoles, 331 | ]; 332 | 333 | $mergedData = array_merge($relationshipValues, $data); 334 | 335 | $this->createUsers($mergedData, $size); 336 | } 337 | 338 | public function prepareArticleDataWithTwoCategories( 339 | array $data = [], 340 | int $size = 1 341 | ) { 342 | $first_category = $this->createCategory(['name' => 'sport']); 343 | $second_category = $this->createCategory(['name' => 'news']); 344 | $miniPrimaryCategory = $this->getMiniCategory($second_category->id); 345 | 346 | $miniCategories = $this->getMiniCategory([$second_category->id, $first_category->id]); 347 | 348 | $relationshipValues = [ 349 | 'primarycategory' => $miniPrimaryCategory, 350 | 'categories' => $miniCategories, 351 | ]; 352 | //dd($relationshipValues); 353 | $mergedData = array_merge($relationshipValues, $data); 354 | 355 | $this->createArticles($mergedData, $size); 356 | } 357 | 358 | /** 359 | * @param string|array $category_id 360 | * @return string|false 361 | * 362 | * @throws Exception 363 | */ 364 | public function getMiniCategory($category_id = '') 365 | { 366 | if (is_array($category_id)) { 367 | $out = []; 368 | foreach ($category_id as $category) { 369 | $out[] = $this->prepareSingleMiniCategory($category); 370 | } 371 | } else { 372 | $out[] = $this->prepareSingleMiniCategory($category_id); 373 | } 374 | 375 | return json_encode($out); 376 | } 377 | 378 | /** 379 | * @param string|array $permission_id 380 | * @return string|false 381 | * 382 | * @throws Exception 383 | */ 384 | public function getMiniPermissions($permission_id = '') 385 | { 386 | if (is_array($permission_id)) { 387 | $out = []; 388 | foreach ($permission_id as $permission) { 389 | $out[] = $this->prepareSingleMiniPermission($permission); 390 | } 391 | } else { 392 | $out[] = $this->prepareSingleMiniPermission($permission_id); 393 | } 394 | 395 | return json_encode($out); 396 | } 397 | 398 | /** 399 | * @param string|array $role_id 400 | * @return string|false 401 | * 402 | * @throws Exception 403 | */ 404 | public function getMiniRoles($role_id = '') 405 | { 406 | if (is_array($role_id)) { 407 | $out = []; 408 | foreach ($role_id as $role) { 409 | $out[] = $this->prepareSingleMiniRole($role); 410 | } 411 | } else { 412 | $out[] = $this->prepareSingleMiniRole($role_id); 413 | } 414 | 415 | return json_encode($out); 416 | } 417 | 418 | /** 419 | * @param $category_id 420 | * @return object 421 | * 422 | * @throws Exception 423 | */ 424 | public function prepareSingleMiniCategory($category_id) 425 | { 426 | if ($category_id == '' || is_null($category_id)) { 427 | $category = $this->createCategory(); 428 | } else { 429 | $category = Category::find($category_id); 430 | if (is_null($category)) { 431 | return null; 432 | } 433 | } 434 | 435 | return (object) [ 436 | 'ref_id' => $category->id, 437 | 'name' => $category->name, 438 | 'slug' => $category->slug, 439 | 'description' => $category->description, 440 | ]; 441 | } 442 | 443 | /** 444 | * @param $permission_id 445 | * @return object 446 | * 447 | * @throws Exception 448 | */ 449 | public function prepareSingleMiniPermission($permission_id) 450 | { 451 | if ($permission_id == '' || is_null($permission_id)) { 452 | $permission = $this->createPermission(); 453 | } else { 454 | $permission = Permission::find($permission_id); 455 | if (is_null($permission)) { 456 | return null; 457 | } 458 | } 459 | 460 | return (object) [ 461 | 'ref_id' => $permission->id, 462 | 'name' => $permission->name, 463 | 'label' => getTranslatedContent($permission->label), 464 | ]; 465 | } 466 | 467 | /** 468 | * @param $role_id 469 | * @return object 470 | * 471 | * @throws Exception 472 | */ 473 | public function prepareSingleMiniRole($role_id) 474 | { 475 | if ($role_id == '' || is_null($role_id)) { 476 | $role = $this->createRole(); 477 | } else { 478 | $role = Role::find($role_id); 479 | if (is_null($role)) { 480 | return null; 481 | } 482 | } 483 | 484 | return (object) [ 485 | 'ref_id' => $role->id, 486 | 'name' => $role->name, 487 | 'label' => $role->label, 488 | ]; 489 | } 490 | 491 | /** 492 | * @param string $autoincrement_id 493 | * @return false|string 494 | */ 495 | public function getMiniArticle(string $autoincrement_id = '') 496 | { 497 | if ($autoincrement_id == '' || is_null($autoincrement_id)) { 498 | $article = $this->createArticle(); 499 | } else { 500 | $article = Article::find($autoincrement_id); 501 | if (is_null($article)) { 502 | return json_encode( 503 | [] 504 | ); 505 | } 506 | } 507 | 508 | return json_encode( 509 | [ 510 | (object) [ 511 | 'ref_id' => $article->id, 512 | 'title' => $article->title, 513 | 'slug' => $article->slug, 514 | 'visibility' => $article->visibility, 515 | 'status' => $article->status, 516 | ], 517 | ] 518 | ); 519 | } 520 | 521 | /** 522 | * @return object 523 | */ 524 | public function getIdNull() 525 | { 526 | return (object) [ 527 | 'id' => null, 528 | ]; 529 | } 530 | 531 | /** 532 | * @param string $navigation_id 533 | * @return false|string 534 | * 535 | * @throws Exception 536 | */ 537 | public function getMiniNavigation(string $navigation_id = '') 538 | { 539 | if ($navigation_id == '' || is_null($navigation_id)) { 540 | $navigation = $this->createNavigation(); 541 | } else { 542 | $navigation = Navigation::find($navigation_id); 543 | if (is_null($navigation)) { 544 | return json_encode( 545 | [] 546 | ); 547 | } 548 | } 549 | 550 | return json_encode( 551 | [ 552 | (object) [ 553 | 'ref_id' => $navigation->id, 554 | 'text' => $navigation->text, 555 | 'code' => $navigation->code, 556 | 'title' => getTranslatedContent($navigation->title), 557 | ], 558 | ] 559 | ); 560 | } 561 | 562 | /** 563 | * @param $navigation 564 | * @return bool 565 | */ 566 | public function isNavigationCreated($navigation) 567 | { 568 | return ! is_null(Navigation::find($navigation->id)); 569 | } 570 | 571 | /** 572 | * @param $item 573 | * @return bool 574 | */ 575 | public function isItemCreated($item) 576 | { 577 | return ! is_null(Item::find($item->id)); 578 | } 579 | 580 | /** 581 | * @param Navigation $navigation 582 | * @param SubItem $sub_item 583 | * 584 | * @throws Exception 585 | */ 586 | public function cleanUp(Navigation $navigation, SubItem $sub_item) 587 | { 588 | $sub_item->delete(); 589 | $navigation->delete(); 590 | } 591 | 592 | /** 593 | * @param $item 594 | * @return bool 595 | */ 596 | public function isItemUpdatedCorrectly($item) 597 | { 598 | return Str::contains($item->name, 'Aggiornato'); 599 | } 600 | 601 | /** 602 | * @param $navigation 603 | * @return bool 604 | */ 605 | public function isNavigationUpdatedCorrectly($navigation) 606 | { 607 | return Str::contains($navigation->text, 'Aggiornato'); 608 | } 609 | 610 | /** 611 | * @param $navigation 612 | * @return bool 613 | */ 614 | public function isUpdated($navigation) 615 | { 616 | return $navigation->text == 'Aggiornato'; 617 | } 618 | 619 | /** 620 | * @param $navigation_id 621 | * @param $item_id 622 | * @return bool 623 | */ 624 | public function isItemAddedInNavigationCollection($navigation_id, $item_id) 625 | { 626 | $navigation = Navigation::where('id', '=', $navigation_id)->get()->first(); 627 | $return = false; 628 | if (isset($navigation->items)) { 629 | foreach ($navigation->items as $item) { 630 | $return = $item->ref_id == $item_id; 631 | } 632 | } 633 | 634 | return $return; 635 | } 636 | 637 | /** 638 | * @param $navigation_code 639 | * @return false|string 640 | */ 641 | public function getNavigation($navigation_code) 642 | { 643 | $arr = []; 644 | $navigation = Navigation::where('code', '=', $navigation_code)->get()->first(); 645 | 646 | $newNavigation = new stdClass; 647 | $newNavigation->ref_id = $navigation->id; 648 | $newNavigation->code = $navigation_code; 649 | $newNavigation->title[cl()] = $navigation->title; 650 | $newNavigation->text = $navigation->text; 651 | 652 | $arr[] = $newNavigation; 653 | 654 | return json_encode($arr); 655 | } 656 | 657 | protected function getMiniSubItem(string $sub_item_id) 658 | { 659 | if ($sub_item_id == '' || is_null($sub_item_id)) { 660 | $sub_item = $this->createSubItems(); 661 | } else { 662 | $sub_item = SubItem::find($sub_item_id); 663 | if (is_null($sub_item)) { 664 | return json_encode( 665 | [] 666 | ); 667 | } 668 | } 669 | 670 | return json_encode( 671 | [ 672 | (object) [ 673 | 'ref_id' => $sub_item->id, 674 | 'code' => $sub_item->code, 675 | 'href' => $sub_item->href, 676 | 'text' => getTranslatedContent($sub_item->text), 677 | ], 678 | ] 679 | ); 680 | } 681 | } 682 | -------------------------------------------------------------------------------- /tests/SyncUtilsTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('', $out); 17 | 18 | // Input not expected Array with key equals to language code Ex. En 19 | 20 | $cl = cl(); 21 | 22 | $mlCollection = [ 23 | $cl => 'car', 24 | ]; 25 | 26 | $out = getTranslatedContent($mlCollection); 27 | 28 | $this->assertEquals('car', $out); 29 | } 30 | 31 | public function test_cl() 32 | { 33 | // Input expected to be 'en' from $cl 34 | 35 | $cl = 'en'; 36 | 37 | $out = cl($cl); 38 | 39 | $this->assertEquals('en', $out); 40 | } 41 | 42 | public function test_ml() 43 | { 44 | // Input expected to be [ "en" => "1" ] from $out 45 | 46 | $destination = null; 47 | $input = '1'; 48 | $out = ml($destination, $input); 49 | $this->assertEquals([ 50 | 'en' => '1', 51 | ], $out); 52 | } 53 | 54 | public function test_isMl() 55 | { 56 | $value = [ 57 | 'is-ml' => true, 58 | ]; 59 | 60 | $out = isML($value); 61 | 62 | $this->assertEquals(true, $out); 63 | 64 | // 65 | 66 | $value = [ 67 | 'is-ml' => false, 68 | ]; 69 | 70 | $out = isML($value); 71 | 72 | $this->assertEquals(false, $out); 73 | 74 | // 75 | 76 | $value = [ 77 | 'is-ml' => '', 78 | ]; 79 | 80 | $out = isML($value); 81 | 82 | $this->assertEquals(false, $out); 83 | } 84 | 85 | public function test_isMD() 86 | { 87 | //Input expected '' from $value 88 | 89 | $value = [ 90 | 'is-md' => true, 91 | ]; 92 | 93 | $out = isMD($value); 94 | $this->assertEquals(true, $out); 95 | 96 | // 97 | $value = [ 98 | 'is-md' => false, 99 | ]; 100 | 101 | $out = isMD($value); 102 | $this->assertEquals(false, $out); 103 | 104 | // 105 | 106 | $value = [ 107 | 'is-md' => '', 108 | ]; 109 | 110 | $out = isMD($value); 111 | $this->assertEquals(false, $out); 112 | } 113 | 114 | public function test_isEM() 115 | { 116 | //Input expected 'EmbedsMany' from $value 117 | 118 | $value = 'EmbedsMany'; 119 | 120 | $out = is_EM($value); 121 | 122 | $this->assertEquals(true, $out); 123 | 124 | //Input expected '' from $value 125 | 126 | $value = ''; 127 | 128 | $out = is_EM($value); 129 | 130 | $this->assertEquals(false, $out); 131 | } 132 | 133 | public function test_isEO() 134 | { 135 | //Input expected 'EmbedsOne' from $value 136 | 137 | $value = 'EmbedsOne'; 138 | 139 | $out = is_EO($value); 140 | 141 | $this->assertEquals(true, $out); 142 | 143 | //Input expected '' from $value 144 | $value = ''; 145 | 146 | $out = is_EO($value); 147 | 148 | $this->assertEquals(false, $out); 149 | } 150 | 151 | public function test_isHM() 152 | { 153 | //Input expected 'Has Many' from $value 154 | 155 | $value = 'HasMany'; 156 | 157 | $out = is_HM($value); 158 | 159 | $this->assertEquals(true, $out); 160 | 161 | //Input expected '' from $value 162 | 163 | $value = ''; 164 | 165 | $out = is_HM($value); 166 | 167 | $this->assertEquals(false, $out); 168 | } 169 | 170 | public function test_isHO() 171 | { 172 | //Input expected 'HasOne' from $value 173 | 174 | $value = 'HasOne'; 175 | 176 | $out = is_HO($value); 177 | 178 | $this->assertEquals(true, $out); 179 | 180 | //Input expected '' from $value 181 | 182 | $value = ''; 183 | 184 | $out = is_HO($value); 185 | 186 | $this->assertEquals(false, $out); 187 | } 188 | 189 | public function test_isEditable() 190 | { 191 | $value = [ 192 | 'is-editable' => false, 193 | ]; 194 | 195 | $out = isEditable($value); 196 | 197 | $this->assertEquals(false, $out); 198 | 199 | // 200 | 201 | $value = [ 202 | 'is-editable' => true, 203 | ]; 204 | 205 | $out = isEditable($value); 206 | 207 | $this->assertEquals(true, $out); 208 | 209 | // 210 | 211 | $value = [ 212 | 'is-editable' => '', 213 | ]; 214 | 215 | $out = isEditable($value); 216 | 217 | $this->assertEquals('', $out); 218 | } 219 | 220 | public function test_hasTarget() 221 | { 222 | $value = [ 223 | 'has-target' => false, 224 | ]; 225 | 226 | $out = hasTarget($value); 227 | 228 | $this->assertEquals(false, $out); 229 | 230 | // 231 | 232 | $value = [ 233 | 'has-target' => true, 234 | ]; 235 | 236 | $out = hasTarget($value); 237 | 238 | $this->assertEquals(true, $out); 239 | 240 | // 241 | 242 | $value = [ 243 | 'has-target' => '', 244 | ]; 245 | 246 | $out = hasTarget($value); 247 | 248 | $this->assertEquals('', $out); 249 | } 250 | 251 | public function test_isFillable() 252 | { 253 | // 254 | $value = []; 255 | $event = ''; 256 | 257 | $out = isFillable($value, $event); 258 | 259 | $this->assertEquals(isEditable($value), $out); 260 | 261 | // 262 | 263 | $value = ''; 264 | $event = 'add'; 265 | 266 | $out = isFillable($value, $event); 267 | 268 | $this->assertEquals(true, $out); 269 | } 270 | 271 | public function test_getAID() 272 | { 273 | //Check if there's no data inside the database, set id to 1 274 | Article::truncate(); 275 | $article = new Article; 276 | 277 | $out = getAID($article); 278 | 279 | $this->assertEquals(1, $out); 280 | 281 | //If there's already data inside the database, increments new data by 1 282 | 283 | $articleModel = $this->prepareArticleData([]); 284 | 285 | $out = getAID($article); 286 | 287 | $this->assertEquals(2, $out); 288 | 289 | Article::truncate(); 290 | } 291 | 292 | public function test_getArrayWithEmptyObj() 293 | { 294 | $article = new Article; 295 | $is_EO = true; 296 | $is_EM = []; 297 | $expectedArray = [ 298 | (object) [ 299 | 'autoincrement_id' => null, 300 | 'title' => null, 301 | 'content' => null, 302 | 'slug' => null, 303 | 'visibility' => null, 304 | 'status' => null, 305 | 'is_deleted' => null, 306 | 'is_active' => null, 307 | ], 308 | ]; 309 | $out = getArrayWithEmptyObj($article, $is_EO, $is_EM); 310 | 311 | $this->assertEquals($expectedArray, $out); 312 | } 313 | 314 | public function test_getCounterForRelationships() 315 | { 316 | $is_EO = true; 317 | $is_EM = false; 318 | $i = null; 319 | $method = ''; 320 | 321 | $out = getCounterForRelationships($method, $is_EO, $is_EM, $i); 322 | 323 | $this->assertEquals('', $out); 324 | 325 | // 326 | 327 | $is_EO = false; 328 | $is_EM = true; 329 | $i = null; 330 | $method = ''; 331 | 332 | $out = getCounterForRelationships($method, $is_EO, $is_EM, $i); 333 | 334 | $this->assertEquals('', $out); 335 | 336 | // 337 | 338 | $is_EO = true; 339 | $is_EM = false; 340 | $i = null; 341 | $method = null; 342 | 343 | $out = getCounterForRelationships($method, $is_EO, $is_EM, $i); 344 | 345 | $this->assertEquals('', $out); 346 | 347 | // 348 | 349 | $is_EO = false; 350 | $is_EM = true; 351 | $i = null; 352 | $method = null; 353 | 354 | $out = getCounterForRelationships($method, $is_EO, $is_EM, $i); 355 | 356 | $this->assertEquals('-'.$i, $out); 357 | } 358 | 359 | public function test_getTypeOnTarget() 360 | { 361 | $EM = [ 362 | 'typeOnTarget' => 'EmbedsMany', 363 | ]; 364 | $EO = [ 365 | 'typeOnTarget' => 'EmbedsOne', 366 | ]; 367 | $defaultValue = []; 368 | 369 | $outEM = getTypeOnTarget($EM); 370 | $outEO = getTypeOnTarget($EO); 371 | $outDefaultValue = getTypeOnTarget($defaultValue); 372 | 373 | $this->assertEquals('EmbedsMany', $outEM); 374 | $this->assertEquals('EmbedsOne', $outEO); 375 | $this->assertEquals('EmbedsMany', $outDefaultValue); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | set('laravel-mongo-auto-sync.model_path', './tests/Models'); 31 | $app['config']->set('laravel-mongo-auto-sync.model_namespace', 'Tests\Models'); 32 | 33 | $config = require 'config/database.php'; 34 | 35 | $app['config']->set('app.key', 'ZsZewWyUJ5FsKp9lMwv4tYbNlegQilM7'); 36 | 37 | $app['config']->set('database.default', 'mongodb'); 38 | $app['config']->set('database.connections.mongodb', $config['connections']['mongodb']); 39 | $app['config']->set('database.connections.mongodb2', $config['connections']['mongodb']); 40 | $app['config']->set('database.connections.dsn_mongodb', $config['connections']['dsn_mongodb']); 41 | $app['config']->set('database.connections.dsn_mongodb_db', $config['connections']['dsn_mongodb_db']); 42 | 43 | $app['config']->set('auth.model', 'User'); 44 | $app['config']->set('auth.providers.users.model', 'User'); 45 | $app['config']->set('cache.driver', 'array'); 46 | 47 | $app['config']->set('queue.default', 'database'); 48 | $app['config']->set('queue.connections.database', [ 49 | 'driver' => 'mongodb', 50 | 'table' => 'jobs', 51 | 'queue' => 'default', 52 | 'expire' => 60, 53 | ]); 54 | $app['config']->set('queue.failed.database', 'mongodb2'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/UpdateWithSyncTest.php: -------------------------------------------------------------------------------- 1 | createSubItems( 20 | [ 21 | 'text' => 'example sub item test', 22 | 'code' => 'HFGRT12345', 23 | 'href' => 'https://google.com', 24 | ] 25 | ); 26 | $this->assertEquals('example sub item test', getTranslatedContent($sub_item->text)); 27 | $this->assertEquals('HFGRT12345', $sub_item->code); 28 | $this->assertEquals('https://google.com', $sub_item->href); 29 | 30 | //Navigation Test 31 | $sub_items = json_encode( 32 | [ 33 | (object) [ 34 | 'ref_id' => $sub_item->id, 35 | 'text' => getTranslatedContent($sub_item->text), 36 | 'code' => $sub_item->code, 37 | 'href' => $sub_item->href, 38 | ], 39 | ] 40 | ); 41 | 42 | $date = Date::now(); 43 | 44 | $navigation = $this->createNavigation( 45 | [ 46 | 'text' => 'example navigation text', 47 | 'code' => '1234ABHFGRT5', 48 | 'href' => 'https://www.netflix.com/browse', 49 | 'date' => $date, 50 | 'target' => '_blank', 51 | 'title' => 'Random title', 52 | 'sub_items' => $sub_items, 53 | ] 54 | ); 55 | 56 | $this->assertTrue($this->isNavigationCreated($navigation)); 57 | $this->assertIsString($navigation->text); 58 | 59 | $this->assertEquals('example navigation text', $navigation->text); 60 | $this->assertEquals('1234ABHFGRT5', $navigation->code); 61 | $this->assertEquals('https://www.netflix.com/browse', $navigation->href); 62 | //$this->assertEquals($date, $navigation->date); TODO: Precision to be fixed 63 | $this->assertEquals('_blank', $navigation->target); 64 | $this->assertEquals('Random title', getTranslatedContent($navigation->title)); 65 | $this->assertInstanceOf(MongoCollection::class, $navigation->sub_items); 66 | 67 | //Check target 68 | $sub_item = SubItem::find($sub_item->id); 69 | $mini_navigation = $sub_item->navigation; 70 | $this->assertNotNull($mini_navigation); 71 | 72 | $this->assertEquals($navigation->id, $mini_navigation->ref_id); 73 | $this->assertEquals('1234ABHFGRT5', $mini_navigation->code); 74 | $this->assertEquals('Random title', getTranslatedContent($mini_navigation->title)); 75 | $this->assertEquals('example navigation text', $navigation->text); 76 | 77 | //clean data 78 | $navigation->delete(); 79 | $sub_item->delete(); 80 | } 81 | 82 | public function test_update_with_embeds_many_on_target() 83 | { 84 | $faker = Factory::create(); 85 | 86 | $navigation = new Navigation; 87 | 88 | $text = $faker->text(50); 89 | $code = $faker->creditCardNumber; 90 | $href = $faker->url; 91 | $date = Date::now(); 92 | $target = $faker->text(50); 93 | $title = null; 94 | $items = json_encode([]); 95 | 96 | $arr = [ 97 | 'text' => $text, 98 | 'code' => $code, 99 | 'href' => $href, 100 | 'date' => $date, 101 | 'target' => $target, 102 | 'title' => $title, 103 | 'sub_items' => $items, 104 | ]; 105 | $request = new Request; 106 | $navigation = $navigation->storeWithSync($request, $arr); 107 | 108 | $this->assertTrue($this->isNavigationCreated($navigation)); 109 | $this->assertEquals($text, $navigation->text); 110 | 111 | $this->assertEquals($text, $navigation->text); 112 | $this->assertEquals($code, $navigation->code); 113 | $this->assertEquals($href, $navigation->href); 114 | // $this->assertEquals($date, $navigation->date); TODO: Precision to be fixed 115 | $this->assertEquals($target, $navigation->target); 116 | $this->assertEquals($title, getTranslatedContent($navigation->title)); 117 | $this->assertInstanceOf(MongoCollection::class, $navigation->sub_items); 118 | //Check target TODO 119 | 120 | $mini_navigation = $this->getMiniNavigation($navigation->id); 121 | 122 | $data = [ 123 | 'navigation' => $mini_navigation, 124 | ]; 125 | 126 | $sub_item = $this->createSubItems($data); 127 | 128 | $this->assertEquals($navigation->id, $sub_item->navigation->ref_id); 129 | $this->assertInstanceOf(MiniNavigation::class, $sub_item->navigation); 130 | 131 | //Check target 132 | $navigation_original = Navigation::find($navigation->id); 133 | 134 | /*$sub_item_mini = $navigation->sub_items[0]; 135 | 136 | $this->assertNotEmpty($navigation_original->sub_items); 137 | $this->assertEquals($sub_item->id, $sub_item_mini->ref_id); 138 | $this->assertEquals($sub_item->text, $sub_item_mini->text); 139 | $this->assertEquals($sub_item->code, $sub_item_mini->code); 140 | $this->assertEquals($sub_item->title, $sub_item_mini->title);*/ 141 | 142 | //Add more sub items and restart test 143 | $sub_items = $this->getMiniSubItem($sub_item->id); 144 | $date = Carbon::now(); 145 | $data = [ 146 | 'text' => 'text_updated', 147 | 'title' => 'title_updated', 148 | 'code' => 'code_updated', 149 | 'href' => 'href_updated', 150 | 'date' => $date, 151 | 'target' => 'target_updated', 152 | 'sub_items' => $sub_items, 153 | ]; 154 | 155 | $options = []; 156 | $request = new Request; 157 | $navigation_updated = $navigation_original->updateWithSync($request, $data, $options); 158 | 159 | $this->assertEquals('text_updated', $navigation_updated->text); 160 | $this->assertEquals('title_updated', getTranslatedContent($navigation_updated->title)); 161 | $this->assertEquals('code_updated', $navigation_updated->code); 162 | $this->assertEquals('href_updated', $navigation_updated->href); 163 | //$this->assertEquals($date,$navigation_updated->date );TODO: fix precision 164 | $this->assertEquals('target_updated', $navigation_updated->target); 165 | $this->assertEquals(1, $navigation_updated->sub_items->count()); 166 | 167 | $mini_navigation = $this->getMiniNavigation($navigation->id); 168 | $data = [ 169 | 'navigation' => $mini_navigation, 170 | ]; 171 | 172 | $sub_item = $this->createSubItems($data); 173 | $navigation = Navigation::find($navigation->id); 174 | 175 | /*$this->assertTrue($navigation->sub_items->count() == 4); 176 | 177 | $sub_item_mini = $navigation->sub_items->where('ref_id', $sub_item->id)->first(); 178 | 179 | $this->assertEquals($sub_item->id, $sub_item_mini->ref_id); 180 | $this->assertEquals($sub_item->text, $sub_item_mini->text); 181 | $this->assertEquals($sub_item->code, $sub_item_mini->code); 182 | $this->assertEquals($sub_item->title, $sub_item_mini->title);*/ 183 | 184 | //clean data 185 | $navigation->delete(); 186 | $sub_item->delete(); 187 | } 188 | 189 | public function test_update_with_partial_request_updating_embeds_many_on_target() 190 | { 191 | $faker = Factory::create(); 192 | 193 | $navigation = new Navigation; 194 | $navigation = $navigation->storeWithSync(new Request, [ 195 | 'text' => $faker->text(50), 196 | 'code' => $faker->creditCardNumber, 197 | 'href' => $faker->url, 198 | 'date' => Date::now(), 199 | 'target' => $faker->text(50), 200 | 'title' => null, 201 | 'sub_items' => json_encode([]), 202 | ]); 203 | 204 | $this->assertTrue($this->isNavigationCreated($navigation)); 205 | $this->assertInstanceOf(MongoCollection::class, $navigation->sub_items); 206 | 207 | // 1 navigation 208 | 209 | $sub_item = $this->createSubItems([ 210 | 'navigation' => $this->getMiniNavigation($navigation->id), 211 | ]); 212 | 213 | $this->assertEquals($navigation->id, $sub_item->navigation->ref_id); 214 | $this->assertInstanceOf(MiniNavigation::class, $sub_item->navigation); 215 | 216 | // 1 navigation and 1 sub item with navigation 217 | 218 | $navigation_original = Navigation::find($navigation->id); 219 | 220 | $sub_items = $this->getMiniSubItem($sub_item->id); 221 | 222 | $navigation_updated = $navigation_original->updateWithSync(new Request, [ 223 | 'text' => 'text_updated', 224 | 'title' => 'title_updated', 225 | 'code' => 'code_updated', 226 | 'href' => 'href_updated', 227 | 'date' => Carbon::now(), 228 | 'target' => 'target_updated', 229 | 'sub_items' => $sub_items, 230 | ], []); 231 | 232 | $this->assertEquals(1, $navigation_updated->sub_items->count()); 233 | 234 | $sub_item = $this->createSubItems([ 235 | 'navigation' => $this->getMiniNavigation($navigation->id), 236 | ]); 237 | 238 | $navigation = Navigation::find($navigation->id); 239 | $this->assertEquals(2, $navigation->sub_items->count()); 240 | 241 | // 1 navigation with 2 sub items and 1 sub item with 1 navigation 242 | 243 | $sub_item->updateWithSync(new Request, [ 244 | 'code' => 'partial_code_update', 245 | ], [ 246 | 'request_type' => 'partial', 247 | ]); 248 | 249 | $navigation = Navigation::find($navigation->id); 250 | $this->assertEquals(2, $navigation->sub_items->count()); 251 | 252 | foreach ($navigation->sub_items as $navigation_sub_item) { 253 | if ($navigation_sub_item->ref_id == $sub_item->id) { 254 | $this->assertEquals('partial_code_update', $navigation_sub_item->code); 255 | } 256 | } 257 | 258 | // 1 navigation with 2 sub items (1 of these updated) and 1 sub item with 1 navigation 259 | 260 | //clean data 261 | $navigation->delete(); 262 | $sub_item->delete(); 263 | } 264 | 265 | public function test_update_with_partial_request_plain_field() 266 | { 267 | //Navigation 268 | $this->partialUpdateNavigation(); 269 | 270 | //SubItems 271 | $this->partialUpdateSubItems(); 272 | } 273 | 274 | public function test_update_with_partial_request_relationship_field() 275 | { 276 | //Navigation 277 | $this->partialUpdateNavigationRelationship(); 278 | 279 | //SubItem 280 | $this->partialUpdateSubItemsRelationship(); 281 | } 282 | 283 | private function partialUpdateNavigation() 284 | { 285 | //Create a navigation and associated to the sub item on creation 286 | $navigation_original = $this->createNavigation(); 287 | 288 | $data = [ 289 | 'text' => 'updated', 290 | 'title' => 'updated', 291 | ]; 292 | 293 | $options = [ 294 | 'request_type' => 'partial', 295 | ]; 296 | $request = new Request; 297 | $navigation_new = $navigation_original->updateWithSync($request, $data, $options); 298 | 299 | //text has been updated? 300 | $this->assertEquals('updated', $navigation_new->text); 301 | $this->assertEquals('updated', $navigation_new->title[cl()]); 302 | 303 | //all the other fields has not been updated? 304 | $this->assertEquals($navigation_original->code, $navigation_new->code); 305 | $this->assertEquals($navigation_original->href, $navigation_new->href); 306 | $this->assertEquals($navigation_original->date, $navigation_new->date); 307 | $this->assertEquals($navigation_original->target, $navigation_new->target); 308 | $this->assertEquals($navigation_original->sub_items, $navigation_new->sub_items); 309 | 310 | //clean data 311 | $navigation_new->delete(); 312 | } 313 | 314 | private function partialUpdateSubItems() 315 | { 316 | //Create a navigation and associated to the sub item on creation 317 | $sub_item_original = $this->createSubItems(); 318 | 319 | $data = [ 320 | 'text' => 'updated', 321 | 'code' => 'updated', 322 | ]; 323 | 324 | $options = [ 325 | 'request_type' => 'partial', 326 | ]; 327 | $request = new Request; 328 | $sub_item_new = $sub_item_original->updateWithSync($request, $data, $options); 329 | 330 | //text has been updated? 331 | $this->assertEquals('updated', $sub_item_new->text[cl()]); 332 | $this->assertEquals('updated', $sub_item_new->code); 333 | 334 | //all the other fields has not been updated? 335 | $this->assertEquals($sub_item_original->navigation->getAttributes(), $sub_item_new->navigation->getAttributes()); 336 | $this->assertEquals($sub_item_original->href, $sub_item_new->href); 337 | 338 | //clean data 339 | $sub_item_new->delete(); 340 | } 341 | 342 | private function test_is_skippable_on_processAllRelationships() 343 | { 344 | $this->markTestSkipped('to be implemented'); 345 | 346 | /* 347 | * 348 | Relation type 349 | "relation" => [ 350 | "type" => "EmbedsMany", 351 | "mode" => "classic", 352 | "model" => "App\Models\MiniRelation", 353 | "has-target" => false 354 | ] 355 | 356 | Request type 357 | [ 358 | 'request_type' => 'partial' 359 | ] 360 | * */ 361 | } 362 | 363 | private function partialUpdateNavigationRelationship() 364 | { 365 | //Create a sub_items and associated to the navigation on update 366 | $navigation_original = $this->createNavigation(); 367 | $sub_item_original = $this->createSubItems(); 368 | 369 | //Test Update from SubItem 370 | $mini_sub_items = $this->getMiniSubItem($sub_item_original->id); 371 | $data = [ 372 | 'sub_items' => $mini_sub_items, 373 | ]; 374 | 375 | $options = [ 376 | 'request_type' => 'partial', 377 | ]; 378 | $request = new Request; 379 | $navigation_updated = $navigation_original->updateWithSync($request, $data, $options); 380 | $mini_sub_item_updated = $navigation_updated->sub_items[0]; 381 | $mini_sub_item_original = json_decode($mini_sub_items)[0]; 382 | 383 | //navigation has been updated? 384 | $this->assertNotEmpty($navigation_updated->sub_items); 385 | $this->assertNotNull($mini_sub_item_updated); 386 | 387 | $this->assertEquals($mini_sub_item_original->ref_id, $mini_sub_item_updated->ref_id); 388 | $this->assertEquals($mini_sub_item_original->text, $mini_sub_item_updated->text[cl()]); 389 | $this->assertEquals($mini_sub_item_original->code, $mini_sub_item_updated->code); 390 | $this->assertEquals($mini_sub_item_original->href, $mini_sub_item_updated->href); 391 | 392 | //all the other fields has not been updated? 393 | $this->assertEquals($navigation_original->title[cl()], $navigation_updated->title[cl()]); 394 | $this->assertEquals($navigation_original->code, $navigation_updated->code); 395 | $this->assertEquals($navigation_original->href, $navigation_updated->href); 396 | $this->assertEquals($navigation_original->title, $navigation_updated->title); 397 | $this->assertEquals($navigation_original->date, $navigation_updated->date); 398 | $this->assertEquals($navigation_original->target, $navigation_updated->target); 399 | 400 | //check target - Sub_item 401 | $sub_item = SubItem::all()->where('id', $sub_item_original->id)->first(); 402 | 403 | $this->assertEquals($navigation_updated->id, $sub_item->navigation->ref_id); 404 | 405 | $sub_item_original->delete(); 406 | $navigation_original->delete(); 407 | } 408 | 409 | private function partialUpdateSubItemsRelationship() 410 | { 411 | $navigation_original = $this->createNavigation(); 412 | $mini_navigation_original = $this->getMiniNavigation($navigation_original->id); 413 | 414 | $sub_item_original = $this->createSubItems(['navigation' => $mini_navigation_original]); 415 | $navigation = $this->createNavigation(); 416 | 417 | $original_count_subitems = $navigation->sub_items->count(); 418 | //Test Update from SubItem 419 | $mini_navigation = $this->getMiniNavigation($navigation->id); 420 | $data = [ 421 | 'navigation' => $mini_navigation, 422 | ]; 423 | 424 | $options = [ 425 | 'request_type' => 'partial', 426 | ]; 427 | $request = new Request; 428 | 429 | $sub_item_updated = $sub_item_original->updateWithSync($request, $data, $options); 430 | 431 | //navigation has been updated? 432 | $this->assertNotEquals($sub_item_original->navigation->getAttributes(), $sub_item_updated->navigation->getAttributes()); 433 | $this->assertNotNull($sub_item_updated->navigation); 434 | 435 | $navigation = Navigation::find($navigation->id); 436 | $updated_count_subitems = $navigation->sub_items->count(); 437 | 438 | $this->assertEquals($navigation->id, $sub_item_updated->navigation->ref_id); 439 | $this->assertEquals($navigation->text, $sub_item_updated->navigation->text); 440 | $this->assertEquals($navigation->code, $sub_item_updated->navigation->code); 441 | $this->assertEquals($navigation->title[cl()], $sub_item_updated->navigation->title[cl()]); 442 | 443 | //all the other fields has not been updated? 444 | $this->assertEquals($sub_item_original->text[cl()], $sub_item_updated->text[cl()]); 445 | $this->assertEquals($sub_item_original->code, $sub_item_updated->code); 446 | $this->assertEquals($sub_item_original->href, $sub_item_updated->href); 447 | 448 | //check target - Navigation 449 | 450 | $this->assertTrue($navigation->sub_items->where('ref_id', $sub_item_updated->id)->count() === 1); 451 | 452 | //check target - Navigation subitem has been detached from navigation original? 453 | $navigation = Navigation::all()->where('id', $navigation_original->id)->first(); 454 | 455 | $this->assertTrue($navigation->sub_items->where('ref_id', $sub_item_updated->id)->count() === 0); 456 | 457 | $this->assertEquals(1, $updated_count_subitems); 458 | 459 | //Update with a target already populated 460 | 461 | $mini_navigation = $this->getMiniNavigation($navigation->id); 462 | $data = [ 463 | 'code' => 'code_updated', 464 | ]; 465 | 466 | $options = [ 467 | 'request_type' => 'partial', 468 | ]; 469 | $request = new Request; 470 | 471 | $sub_item_updated = $sub_item_original->updateWithSync($request, $data, $options); 472 | 473 | $navigation = Navigation::find($navigation->id); 474 | $updated_count_subitems = $navigation->sub_items->count(); 475 | 476 | $this->assertEquals($sub_item_updated->code, 'code_updated'); 477 | $this->assertEquals(1, $updated_count_subitems); 478 | 479 | $subitem = $navigation->sub_items->first(); 480 | $this->assertEquals($subitem->code, 'code_updated'); 481 | 482 | $sub_item_original->delete(); 483 | $navigation->delete(); 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /tests/config/database.php: -------------------------------------------------------------------------------- 1 | [ 10 | 11 | 'mongodb' => [ 12 | 'name' => 'mongodb', 13 | 'driver' => 'mongodb', 14 | 'host' => $mongoHost, 15 | 'database' => env('MONGO_DATABASE', 'unittest'), 16 | ], 17 | 18 | 'dsn_mongodb' => [ 19 | 'driver' => 'mongodb', 20 | 'dsn' => "mongodb://$mongoHost:$mongoPort", 21 | 'database' => env('MONGO_DATABASE', 'unittest'), 22 | ], 23 | 24 | 'dsn_mongodb_db' => [ 25 | 'driver' => 'mongodb', 26 | 'dsn' => "mongodb://$mongoHost:$mongoPort/".env('MONGO_DATABASE', 'unittest'), 27 | ], 28 | 29 | 'mysql' => [ 30 | 'driver' => 'mysql', 31 | 'host' => env('MYSQL_HOST', 'mysql'), 32 | 'port' => $mysqlPort, 33 | 'database' => env('MYSQL_DATABASE', 'unittest'), 34 | 'username' => env('MYSQL_USERNAME', 'root'), 35 | 'password' => env('MYSQL_PASSWORD', ''), 36 | 'charset' => 'utf8', 37 | 'collation' => 'utf8_unicode_ci', 38 | 'prefix' => '', 39 | ], 40 | ], 41 | 42 | ]; 43 | -------------------------------------------------------------------------------- /tests/config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION'), 6 | 7 | 'connections' => [ 8 | 9 | 'database' => [ 10 | 'driver' => 'mongodb', 11 | 'table' => 'jobs', 12 | 'queue' => 'default', 13 | 'expire' => 60, 14 | ], 15 | 16 | ], 17 | 18 | 'failed' => [ 19 | 'database' => env('MONGO_DATABASE'), 20 | 'table' => 'failed_jobs', 21 | ], 22 | 23 | ]; 24 | --------------------------------------------------------------------------------