├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ └── feature.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── model-repository.php ├── phpunit.xml.dist ├── src ├── BaseRepository.php ├── Console │ ├── Concerns │ │ └── CommandExceptionHandler.php │ ├── Repository.php │ └── stubs │ │ └── repository.stub ├── Contracts │ └── RepositoryContract.php ├── Facades │ └── ModelRepository.php └── ModelRepositoryServiceProvider.php └── tests ├── App ├── Models │ ├── Profile.php │ └── User.php ├── Repositories │ ├── ProfileRepository.php │ └── TestRepository.php └── database │ └── migrations │ ├── 2014_10_12_000000_create_users_table.php │ └── 2014_10_12_000001_create_profiles_table.php ├── CommandTest.php ├── RepositoryTest.php ├── Traits ├── FileHelpers.php ├── LaravelSetup.php └── LaravelTestBootstrapping.php └── config └── model-repository.php /.gitattributes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/touhidurabir/laravel-model-repository/bd770e8e44467bc90dc3fcc0ec81a8ea5c6bf871/.gitattributes -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug 2 | description: File a bug report 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Before opening a bug report, please search for the behaviour in the existing issues. 8 | 9 | --- 10 | 11 | Thank you for taking the time to file a bug report. To address this bug as fast as possible, we need some information. 12 | - type: input 13 | id: os 14 | attributes: 15 | label: Operating system 16 | description: "Which operating system do you use? Please provide the version as well." 17 | placeholder: "macOS Big Sur 11.5.2" 18 | validations: 19 | required: true 20 | - type: input 21 | id: laravel 22 | attributes: 23 | label: Laravel Version 24 | description: "Please provide the full Laravel version of your project." 25 | placeholder: "v8.6.1" 26 | validations: 27 | required: true 28 | - type: input 29 | id: php 30 | attributes: 31 | label: PHP Version 32 | description: "Please provide the full PHP version that beign used." 33 | placeholder: "PHP 8.0.7, built: Jun 4 2021 01:50:04" 34 | validations: 35 | required: true 36 | - type: dropdown 37 | id: location 38 | attributes: 39 | label: Project Location 40 | description: Where is the project located? 41 | options: 42 | - Local 43 | - Remote 44 | - Somewhere else (please specify in the description!) 45 | validations: 46 | required: true 47 | - type: textarea 48 | id: bug-description 49 | attributes: 50 | label: Bug description 51 | description: What happened? 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: steps 56 | attributes: 57 | label: Steps to reproduce 58 | description: Which steps do we need to take to reproduce this error? 59 | - type: textarea 60 | id: logs 61 | attributes: 62 | label: Relevant log output 63 | description: If applicable, provide relevant log output. No need for backticks here. 64 | render: shell -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Propose a new feature for laravel-model-repository package 3 | title: "[Feature Request]: " 4 | labels: [feature request] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for proposing a feature for laravel-model-repository package. 10 | - type: textarea 11 | id: feature-description 12 | attributes: 13 | label: Feature Description 14 | description: How should this feature look like? 15 | placeholder: Location in the software, usage, output ... 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: valuable 20 | attributes: 21 | label: Is this feature valuable for other users as well and why? 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /vendor 3 | /build 4 | composer.lock 5 | .phpunit.result.cache -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | - 7.4 6 | - 8.0 7 | 8 | env: 9 | matrix: 10 | - COMPOSER_FLAGS="--prefer-lowest" 11 | - COMPOSER_FLAGS="" 12 | 13 | before_script: 14 | - travis_retry composer update ${COMPOSER_FLAGS} 15 | 16 | script: 17 | - vendor/bin/phpunit -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [Unreleased] 5 | 6 | ## [1.0.0] - 2021-08-21 7 | - Initial release 8 | 9 | ## [1.0.1] - 2021-08-24 10 | - Test Updates 11 | 12 | ## [1.0.2] - 2021-08-29 13 | - Bug fix 14 | 15 | ## [1.1.0] - 2021-09-08 16 | - Removed the manual stub generation process 17 | - Added the touhidurabir/laravel-stub-generator package as the stub generator process handler 18 | - Added replaceable feature to repository command 19 | - Bug Fix 20 | - Readme Update 21 | 22 | 23 | ## [1.1.1] - 2021-09-08 24 | - Bug Fix 25 | - Test Update 26 | 27 | ## [1.1.2] - 2021-10-14 28 | - Bug Fix 29 | - Readme Update 30 | 31 | ## [1.1.3] - 2021-10-27 32 | - Tests update 33 | - Readme Update 34 | - Issue Template added 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Touhidur Rahman Abir 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Model Repository 2 | 3 | A simple package to use **Repository Pattern** approach for laravel models . 4 | 5 | ## Repository pattern 6 | Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer. [Microsoft](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design) 7 | 8 | ## Installation 9 | 10 | Require the package using composer: 11 | 12 | ```bash 13 | composer require touhidurabir/laravel-model-repository 14 | ``` 15 | 16 | To publish the config file: 17 | ```bash 18 | php artisan vendor:publish --provider="Touhidurabir\ModelRepository\ModelRepositoryServiceProvider" --tag=config 19 | ``` 20 | 21 | ## Command and Configuration 22 | 23 | To use this package, you need to have repository class bound to laravel model class . This package includes a command that make it easy to to create repository classes from command line . to create a new repository class, run the following command 24 | 25 | ```bash 26 | php artisan make:repository UserRepository --model=User 27 | ``` 28 | 29 | The above command will create a new repository **UserRepository** class in **App\Repositories** path . the **--model** option to define which laravel model class to target for this repositoty class . The content of **UserRepository** will look like 30 | 31 | ```php 32 | namespace App\Repositories; 33 | 34 | use Touhidurabir\ModelRepository\BaseRepository; 35 | use App\Models\User; 36 | 37 | class UserRepository extends BaseRepository { 38 | 39 | /** 40 | * Constructor to bind model to repo 41 | * 42 | * @param object $user 43 | * @return void 44 | */ 45 | public function __construct(User $user) { 46 | 47 | $this->model = $user; 48 | 49 | $this->modelClass = get_class($user); 50 | } 51 | 52 | } 53 | ``` 54 | 55 | This package by default assume all **models** are located in path **App\Models** and use the path **App\Repositories** to store the **repository** classes. But also possible to provide custom repositories class path and different model class path . for example 56 | 57 | ```bash 58 | php artisan make:repository App\\SomeOtherPath\\UserRepository --model=App\\OtherModelPath\\User 59 | ``` 60 | The above command will try to store the repository class to path **App\SomeOtherPath** and will create a directory named **SomeOtherPath** if not already exists. Will also try to resolve model path/namespace from **App\OtherModelPath** . 61 | 62 | Check the **config** file after publishing at the **config/model-repository.php** to see the default settings configurations . 63 | 64 | It is also possible to replace an existing repository file via the command when passing the flag **--replace** 65 | 66 | ```bash 67 | php artisan make:repository UserRepository --model=User --replace 68 | ``` 69 | 70 | The above command will replace the already exiting **UserRepository.php** at the given path with newly generated one. 71 | 72 | > Be ery carefull of the replacing ability as if your repository class contains any custom code, that will be fully replaced with a newly generated file and those custom changes will be lost. 73 | ## Usage 74 | 75 | The best way to use the repository classes via **Dependency Injection** through the **controller** classes . for example : 76 | 77 | ```php 78 | namespace App\Http\Controllers; 79 | 80 | use App\Http\Controllers\Controller; 81 | use App\Repositories\UserRepository; 82 | 83 | class UserController extends Controller { 84 | 85 | /** 86 | * The resource repository instance 87 | * 88 | * @var mixed 89 | */ 90 | protected $userRepository; 91 | 92 | /** 93 | * create a new controller instance 94 | * 95 | * @param \App\Repositories\UserRepository $userRepository 96 | * @return void 97 | */ 98 | public function __construct(UserRepository $userRepository) { 99 | 100 | $this->userRepository = $userRepository; 101 | } 102 | } 103 | ``` 104 | 105 | And in that way one can already get a fully qualified user repository class . Also to manually initiated : 106 | 107 | ```php 108 | namespace App\Http\Controllers; 109 | 110 | use App\Models\User; 111 | use App\Http\Controllers\Controller; 112 | use App\Repositories\UserRepository; 113 | ... 114 | 115 | $userRepository = new UserRepository(new User); 116 | ``` 117 | 118 | Or through static constructor 119 | ```php 120 | $userRepository = UserRepository::withModel(new User); 121 | ``` 122 | 123 | The repository class will have following features/abilities . 124 | 125 | ### Create 126 | 127 | To create a new model record, just call the **create** method on repositoty class and pass the data attributes as : 128 | 129 | ```php 130 | $this->userRepository->create([ 131 | ... 132 | ]); 133 | ``` 134 | 135 | ### Update 136 | 137 | To update a existing model record, call the **update** method of the repository class . the **update** method will require 2 params , the data attributes and the model redored primary key value or an exiting model instance . 138 | 139 | To update with primary key for user with primary key of id with value 10 140 | 141 | ```php 142 | $primaryKeyValue = 10; 143 | 144 | $this->userRepository->update([ 145 | ... 146 | ], $primaryKeyValue); 147 | ``` 148 | or To update the already retrived model record : 149 | 150 | ```php 151 | $user; // the already retrived model record instance 152 | 153 | $this->userRepository->update([ 154 | ... 155 | ], $user); 156 | ``` 157 | 158 | ### Find 159 | 160 | To find a model record, use the **find** method of the repository class 161 | 162 | ```php 163 | $this->userRepository->find(1); // find the id(primary key) of 1 164 | $this->userRepository->find([1,2,3]); // find the id(primary key) of 1,2 and 3 165 | ``` 166 | 167 | The **find** method can also work with array where it will use those as **AND WHERE** query and return the first record that match 168 | 169 | ```php 170 | $this->userRepository->find(['email' => 'somemail@mail.test']); 171 | ``` 172 | 173 | By passing the optional relations array as the second argument to **find** method will load the relations along with model record 174 | 175 | ```php 176 | $this->userRepository->find(1, ['profile']); // find the id(primary key) of 1 177 | $this->userRepository->find([1,2,3], ['profile']); // find the id(primary key) of 1,2 and 3 178 | ``` 179 | 180 | The thrid agument is a optional boolen which is by default set to **false** . By setting it to **true**, it will thorw the **\Illuminate\Database\Eloquent\ModelNotFoundException** when a model record not found . 181 | 182 | ```php 183 | $this->userRepository->find(1, ['profile'], true); // find the id(primary key) of 1 184 | $this->userRepository->find([1,2,3], [], true); // find the id(primary key) of 1,2 and 3 185 | ``` 186 | 187 | ### All Records 188 | 189 | To get back all records, use the **all** method of repository class 190 | 191 | ```php 192 | $this->userRepository->all(); 193 | ``` 194 | 195 | ### Delete 196 | 197 | To Delete a model record, use the **delete** method of repository class 198 | 199 | ```php 200 | $this->userRepository->delete(1); 201 | ``` 202 | 203 | The **delete** method can wrok with model instance or the same kind of argument passed to the repository class **find** method . 204 | 205 | ```php 206 | $this->userRepository->delete($user); // delete the alredt retrived $user model instance 207 | $this->userRepository->delete(1); // delete user id of 1 208 | $this->userRepository->delete([1,2,3]); // delete user id of 1,2 and 3 209 | $this->userRepository->delete(['email' => 'somemail@mail.test']); // delete user with email of somemail@mail.test 210 | ``` 211 | 212 | The **delete** method also check for the **SoftDelete** feature , that is if the model is using the **Illuminate\Database\Eloquent\SoftDeletes** trait, the it will do the soft delete of given model records. 213 | 214 | ### Force Delete 215 | 216 | To Force Delete a model record, use the **forceDelete** method of repository class 217 | 218 | ```php 219 | $this->userRepository->forceDelete(1); 220 | ``` 221 | 222 | The **delete** method can wrok with model instance or the same kind of argument passed to the repository class **find** method . 223 | 224 | ```php 225 | $this->userRepository->forceDelete($user); // delete the alredt retrived $user model instance 226 | $this->userRepository->forceDelete(1); // delete user id of 1 227 | $this->userRepository->forceDelete([1,2,3]); // delete user id of 1,2 and 3 228 | $this->userRepository->forceDelete(['email' => 'somemail@mail.test']); // delete user with email of somemail@mail.test 229 | ``` 230 | 231 | The **delete** method also check for the **SoftDelete** feature, that is regardless of the model is using the **Illuminate\Database\Eloquent\SoftDeletes** trait, the it will remove those records from DB. 232 | 233 | ### Restore 234 | 235 | To Restore a model record that has soft deleted, use the **forceDelete** method of repository class 236 | 237 | ```php 238 | $this->userRepository->restore(1); 239 | ``` 240 | 241 | The **restore** will only works for those models that use the **SoftDeletes** feature . It try to use the restore on the model that do not have **SoftDeletes** implemented, it will throw an exception. 242 | 243 | The **restore** method can wrok with model instance or array of model primary keys . 244 | 245 | ```php 246 | $this->userRepository->restore($user); // restore the already retrived $user model instance 247 | $this->userRepository->restore(1); // restore user id of 1 248 | $this->userRepository->restore([1,2,3]); // restore user id of 1,2 and 3test 249 | ``` 250 | 251 | ## Other Features 252 | 253 | ### Get Model 254 | 255 | As this package does not handle all of the features of Eloquent and if any other Eloquent method need to use to build complex query, we need the model instance . to get the model instance 256 | 257 | ```php 258 | $this->userRepository->getModel(); 259 | ``` 260 | 261 | Also to set/update the model later 262 | 263 | ```php 264 | $this->userRepository->setModel(new User); 265 | $this->userRepository->setModel($user); 266 | ``` 267 | 268 | ### Model Sanitizer 269 | 270 | The BaseRepository class includes a model sanitizer that will automatically sanitize passed array attributes on model record create/update . Here sanatize means it will remove any element from the data array to match with the model table schema while at the same time respecting model **$fillable** and **$hidden** properties . 271 | 272 | The implementation of these methods are as such 273 | 274 | ```php 275 | /** 276 | * Sanitize data list to model fillables 277 | * 278 | * @param array $data 279 | * @return array 280 | */ 281 | public function sanitizeToModelFillable(array $data) { 282 | 283 | $classModel = $this->model->getModel(); 284 | $fillable = $classModel->getFillable(); 285 | 286 | $fillables = ! empty($fillable) 287 | ? $fillable 288 | : array_diff( 289 | array_diff( 290 | Schema::getColumnListing($classModel->getTable()), 291 | $classModel->getGuarded() 292 | ), 293 | $classModel->getHidden() 294 | ); 295 | 296 | return array_intersect_key($data, array_flip($fillables)); 297 | } 298 | ``` 299 | 300 | So even if extra details passed, it will be ignored or some columns passed that in the **$fillable** or **$hidden** list. 301 | 302 | ```php 303 | $user = $this->userRepository->create([ 304 | 'name' => 'User Name', 305 | 'email' => 'somemail@mail.test', 306 | 'password' => Hash::make('password'), 307 | 'date_of_birth' => '1990-12-08' // This date_of_birth column not present in users table 308 | ]); 309 | ``` 310 | 311 | The above code will run without any issue while a simple model create method will throw exception . 312 | 313 | ```php 314 | $user = $this->userRepository->create($request->validated()); 315 | 316 | $profile = $this->profileRepository->create($request->validated()); 317 | ``` 318 | 319 | This become very useful when in one single controller method do need to push data to multiple model table 320 | 321 | ## Contributing 322 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 323 | 324 | Please make sure to update tests as appropriate. 325 | 326 | ## License 327 | [MIT](./LICENSE.md) 328 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "touhidurabir/laravel-model-repository", 3 | "description": "A package to implement repository pattern for laravel models", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Touhidur Rahman", 9 | "email": "abircse06@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.2.0", 14 | "touhidurabir/laravel-stub-generator": "^1.0" 15 | }, 16 | "autoload" : { 17 | "psr-4" : { 18 | "Touhidurabir\\ModelRepository\\": "src/" 19 | } 20 | }, 21 | "autoload-dev" : { 22 | "psr-4" : { 23 | "Touhidurabir\\ModelRepository\\Tests\\": "tests/" 24 | } 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^9.5", 28 | "orchestra/testbench": "^6.20", 29 | "illuminate/container": "^8.54", 30 | "illuminate/support": "^8.54", 31 | "illuminate/database": "^8.54" 32 | }, 33 | "extra": { 34 | "laravel": { 35 | "providers": [ 36 | "Touhidurabir\\ModelRepository\\ModelRepositoryServiceProvider" 37 | ], 38 | "aliases": { 39 | "ModelRepository": "Touhidurabir\\ModelRepository\\Facades\\ModelRepository" 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /config/model-repository.php: -------------------------------------------------------------------------------- 1 | \Touhidurabir\ModelRepository\BaseRepository::class, 16 | 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default model namespace prefix 21 | |-------------------------------------------------------------------------- 22 | | 23 | | The base model path which will be used to get the full namespace of the 24 | | give model for which the repository class will be genrated . 25 | | 26 | */ 27 | 28 | 'models_namespace' => 'App\\Models', 29 | 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Path/location namespace to save repository classes 34 | |-------------------------------------------------------------------------- 35 | | 36 | | location where to store the repository classes to save/store. 37 | | 38 | */ 39 | 40 | 'repositories_namespace' => 'App\\Repositories', 41 | 42 | ]; -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 22 | 23 | 24 | src/ 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/BaseRepository.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | protected $model; 19 | 20 | 21 | /** 22 | * The target model class 23 | * 24 | * @var string 25 | */ 26 | protected $modelClass; 27 | 28 | 29 | /** 30 | * comparison operator 31 | * 32 | * @var string 33 | */ 34 | protected $comparisonOperator = '='; 35 | 36 | 37 | /** 38 | * Determine if array has all the keys numeric 39 | * 40 | * @param array $array 41 | * @return bool 42 | */ 43 | protected function arrayHasAllNumericKeys(array $array) { 44 | 45 | return !(count(array_filter(array_keys($array), 'is_string')) > 0); 46 | } 47 | 48 | 49 | /** 50 | * Get the model class full namespace path 51 | * 52 | * @return string 53 | */ 54 | public function getModelClass() { 55 | 56 | if ( $this->modelClass ) { 57 | 58 | return $this->modelClass; 59 | } 60 | 61 | if ( ! $this->model ) { 62 | 63 | $this->modelClass = get_class($this->model); 64 | } 65 | 66 | return $this->modelClass; 67 | } 68 | 69 | 70 | /** 71 | * Sanitize data list to model fillables 72 | * 73 | * @param array $data 74 | * @return array 75 | */ 76 | public function sanitizeToModelFillable(array $data) { 77 | 78 | $classModel = $this->model->getModel(); 79 | $fillable = $classModel->getFillable(); 80 | 81 | $fillables = ! empty($fillable) 82 | ? $fillable 83 | : array_diff( 84 | array_diff( 85 | Schema::getColumnListing($classModel->getTable()), 86 | $classModel->getGuarded() 87 | ), 88 | $classModel->getHidden() 89 | ); 90 | 91 | return array_intersect_key($data, array_flip($fillables)); 92 | } 93 | 94 | 95 | /** 96 | * Get the extra data that passed to model to create/update 97 | * 98 | * @param array $data 99 | * @return array 100 | */ 101 | public function extraData(array $data) { 102 | 103 | $modelFillables = $this->sanitizeToModelFillable($data); 104 | 105 | return array_diff_key($data, $modelFillables); 106 | } 107 | 108 | 109 | /** 110 | * Does the repository model has soft deleted feature enabled; 111 | * 112 | * @return boolean 113 | */ 114 | public function hasSoftDelete() { 115 | 116 | if ( !$this->modelClass && !$this->getModelClass() ) { 117 | 118 | return false; 119 | } 120 | 121 | return in_array( 122 | 'Illuminate\Database\Eloquent\SoftDeletes', 123 | class_uses($this->modelClass) 124 | ); 125 | } 126 | 127 | 128 | /** 129 | * custom method to bind model to repo 130 | * 131 | * @param object<\Illuminate\Database\Eloquent\Model> $model 132 | * @return $this 133 | */ 134 | public function setModel(Model $model) { 135 | 136 | $this->model = $model; 137 | 138 | return $this; 139 | } 140 | 141 | 142 | /** 143 | * Return binded model instance 144 | * 145 | * @return object<\Illuminate\Database\Eloquent\Model> 146 | */ 147 | public function getModel() { 148 | 149 | return $this->model; 150 | } 151 | 152 | 153 | /** 154 | * Static Factory Method 155 | * 156 | * Initiate the class using static factory method 157 | * Userful for more redability 158 | * Also if the inherited class constructor set to protected or private 159 | * 160 | * @param object<\Illuminate\Database\Eloquent\Model> $model 161 | * @return $this 162 | */ 163 | public static function withModel(Model $model) { 164 | 165 | return new static($model); 166 | } 167 | 168 | 169 | /** 170 | * update the find comparison operator 171 | * 172 | * @param string $operator 173 | * @return $this 174 | */ 175 | public function setComparisor(string $operator = '=') { 176 | 177 | $this->comparisonOperator = $operator; 178 | 179 | return $this; 180 | } 181 | 182 | 183 | /** 184 | * Find specific model instance 185 | * 186 | * @param mixed $param 187 | * @param array $withs 188 | * @param bool $allowException 189 | * 190 | * @return mixed 191 | */ 192 | public function find($param, array $withs = [], bool $allowException = false) { 193 | 194 | $find = $allowException ? 'findOrFail' : 'find'; 195 | $first = $allowException ? 'firstOrFail' : 'first'; 196 | 197 | if ( !is_array($param) || $this->arrayHasAllNumericKeys($param) ) { 198 | 199 | return $this->model->with($withs)->{$find}($param); 200 | } 201 | 202 | return $this->where($param)->with($withs)->getModel()->{$first}(); 203 | } 204 | 205 | 206 | /** 207 | * Model order by clause 208 | * 209 | * @param array $orders 210 | * @return $this 211 | */ 212 | public function orderBy(array $orders = ['id' => 'asc']) { 213 | 214 | $existingOrders = $this->model->getQuery()->orders ?? []; 215 | 216 | foreach ($orders as $column => $direction) { 217 | 218 | if ( ! in_array(compact("column", "direction"), $existingOrders) ) { 219 | 220 | $this->model = $this->model->orderBy($column, $direction); 221 | } 222 | } 223 | 224 | return $this; 225 | } 226 | 227 | 228 | /** 229 | * Model egear load clause 230 | * 231 | * @param array $with 232 | * @return $this 233 | */ 234 | public function with($with = []) { 235 | 236 | if ( ! empty($with) ) { 237 | 238 | $this->model = $this->getModel()->with($with); 239 | } 240 | 241 | return $this; 242 | } 243 | 244 | 245 | /** 246 | * Model Pagination 247 | * 248 | * @param int $perPage 249 | * @param array $columns 250 | * @param string $pageName 251 | * @param int $page 252 | * 253 | * @return mixed 254 | */ 255 | public function paginate( int $perPage = null, 256 | array $columns = ['*'], 257 | string $pageName = 'page', 258 | int $page = null 259 | ) { 260 | 261 | return $this->orderBy()->getModel()->paginate( 262 | $perPage ?? 15, 263 | $columns, 264 | $pageName, 265 | $page ?? 1 266 | ); 267 | } 268 | 269 | 270 | /** 271 | * Attach where clause 272 | * 273 | * @param array $constrains 274 | * @return $this 275 | */ 276 | public function where(array $constrains = []) { 277 | 278 | foreach ($constrains as $column => $value) { 279 | 280 | $this->model = $this->model->where($column, $value); 281 | } 282 | 283 | return $this; 284 | } 285 | 286 | 287 | /** 288 | * Get all instances of model 289 | * 290 | * @param array $withs 291 | * @return mixed 292 | */ 293 | public function all(array $withs = []) { 294 | 295 | return $this->orderBy()->getModel()->with($withs)->get(); 296 | } 297 | 298 | 299 | /** 300 | * Create a new record in the database 301 | * 302 | * @param array $data 303 | * @return mixed 304 | */ 305 | public function create(array $data) { 306 | 307 | return $this->model->create($this->sanitizeToModelFillable($data)); 308 | } 309 | 310 | 311 | /** 312 | * Update record in the database 313 | * 314 | * @param array $data 315 | * @param mixed $prinamryKeyValue 316 | * 317 | * @return mixed 318 | */ 319 | public function update(array $data, $prinamryKeyValue) { 320 | 321 | $recordToUpdate = $prinamryKeyValue instanceof Model 322 | ? $prinamryKeyValue 323 | : $this->find($prinamryKeyValue); 324 | 325 | $recordToUpdate->update($this->sanitizeToModelFillable($data)); 326 | 327 | return $recordToUpdate->fresh(); 328 | 329 | } 330 | 331 | 332 | /** 333 | * Delete a database record 334 | * 335 | * @param mixed $param 336 | * @return mixed 337 | */ 338 | public function delete($param) { 339 | 340 | $resource = $param instanceof Model ? $param : $this->find($param); 341 | 342 | if ( $resource instanceof Collection ) { 343 | 344 | if ( $this->hasSoftDelete() ) { 345 | 346 | $resource->toQuery()->update([ 347 | $this->model->getDeletedAtColumn() => now() 348 | ]); 349 | } 350 | 351 | $resource->toQuery()->delete(); 352 | 353 | return true; 354 | } 355 | 356 | return $resource->delete(); 357 | } 358 | 359 | 360 | /** 361 | * Force Delete a database record 362 | * 363 | * @param mixed $param 364 | * @return boolean 365 | */ 366 | public function forceDelete($param) { 367 | 368 | $resource = $param instanceof Model ? $param : $this->find($param); 369 | 370 | if ( $resource instanceof Collection ) { 371 | 372 | $resource->toQuery()->delete(); 373 | 374 | return true; 375 | } 376 | 377 | return $this->hasSoftDelete() ? $resource->forceDelete() : $resource->delete(); 378 | } 379 | 380 | 381 | /** 382 | * Restore a soft deleted database record 383 | * 384 | * @param mixed $param 385 | * @return boolean 386 | */ 387 | public function restore($param) { 388 | 389 | if ( ! $this->hasSoftDelete() ) { 390 | 391 | throw new \Exception('The ' . $this->getModelClass() . ' class does not use soft delete feature.'); 392 | } 393 | 394 | $resource = $param instanceof Model ? $param : $this->model->onlyTrashed->findorFail($param); 395 | 396 | if ( $resource instanceof Collection ) { 397 | 398 | $resource->toQuery()->update([ 399 | $this->model->getDeletedAtColumn() => null 400 | ]); 401 | 402 | return true; 403 | } 404 | 405 | return $resource->restore(); 406 | } 407 | 408 | } -------------------------------------------------------------------------------- /src/Console/Concerns/CommandExceptionHandler.php: -------------------------------------------------------------------------------- 1 | error($message ?? 'Exception Arise During Task Execution'); 21 | 22 | $this->error( 23 | 'Exception : ' 24 | . $exception->getMessage() 25 | . " - " 26 | . $exception->getFile() 27 | . " at line " 28 | . $exception->getLine() 29 | ); 30 | } 31 | } -------------------------------------------------------------------------------- /src/Console/Repository.php: -------------------------------------------------------------------------------- 1 | info('Creating repository class'); 84 | 85 | try { 86 | 87 | $this->classStorePath = $this->generateFilePathFromNamespace( 88 | $this->resolveClassNamespace( 89 | $this->argument('class') 90 | ) ?? config('model-repository.repositories_namespace') 91 | ); 92 | 93 | $saveStatus = (new StubGenerator) 94 | ->from($this->generateFullPathOfStubFile($this->stubPath), true) 95 | ->to($this->classStorePath, true) 96 | ->as($this->resolveClassName($this->argument('class'))) 97 | ->withReplacers([ 98 | 'class' => $this->resolveClassName($this->argument('class')), 99 | 'model' => $this->resolveClassName($this->option('model')), 100 | 'modelInstance' => lcfirst($this->resolveClassName($this->option('model'))), 101 | 'modelNamespace' => $this->resolveClassNamespace($this->option('model')) ?? config('model-repository.models_namespace'), 102 | 'baseClass' => config('model-repository.base_class'), 103 | 'baseClassName' => last(explode('\\', config('model-repository.base_class'))), 104 | 'classNamespace' => $this->resolveClassNamespace($this->argument('class')) ?? config('model-repository.repositories_namespace'), 105 | ]) 106 | ->replace($this->option('replace')) 107 | ->save(); 108 | 109 | if ( $saveStatus ) { 110 | 111 | $this->info('Repository class generated successfully'); 112 | } 113 | 114 | } catch (Throwable $exception) { 115 | 116 | $this->outputConsoleException($exception); 117 | 118 | return 1; 119 | } 120 | } 121 | 122 | 123 | /** 124 | * Genrate the stub file full absolute path 125 | * 126 | * @param string $stubRelativePath 127 | * @return string 128 | */ 129 | protected function generateFullPathOfStubFile(string $stubRelativePath) { 130 | 131 | return __DIR__ . $stubRelativePath; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/Console/stubs/repository.stub: -------------------------------------------------------------------------------- 1 | ${{modelInstance}} 14 | * @return void 15 | */ 16 | public function __construct({{model}} ${{modelInstance}}) { 17 | 18 | $this->model = ${{modelInstance}}; 19 | 20 | $this->modelClass = get_class(${{modelInstance}}); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Contracts/RepositoryContract.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole() ) { 18 | $this->commands([ 19 | Repository::class 20 | ]); 21 | } 22 | 23 | $this->publishes([ 24 | __DIR__.'/../config/model-repository.php' => base_path('config/model-repository.php'), 25 | ], 'config'); 26 | } 27 | 28 | 29 | /** 30 | * Register any application services. 31 | * 32 | * @return void 33 | */ 34 | public function register() { 35 | 36 | $this->mergeConfigFrom( 37 | __DIR__.'/../config/model-repository.php', 'model-repository' 38 | ); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /tests/App/Models/Profile.php: -------------------------------------------------------------------------------- 1 | $profile 14 | * @return void 15 | */ 16 | public function __construct(Profile $profile) { 17 | 18 | $this->model = $profile; 19 | 20 | $this->modelClass = get_class($profile); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tests/App/Repositories/TestRepository.php: -------------------------------------------------------------------------------- 1 | $test 14 | * @return void 15 | */ 16 | public function __construct(Test $test) { 17 | 18 | $this->model = $test; 19 | 20 | $this->modelClass = get_class($test); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tests/App/database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | getSchemaBuilder()->create('users', function (Blueprint $table) { 16 | $table->increments('id'); 17 | $table->string('email')->unique(); 18 | $table->string('password', 60); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | DB::connection()->getSchemaBuilder()->dropIfExists('users'); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/App/database/migrations/2014_10_12_000001_create_profiles_table.php: -------------------------------------------------------------------------------- 1 | getSchemaBuilder()->create('profiles', function (Blueprint $table) { 16 | $table->increments('id'); 17 | $table->string('first_name'); 18 | $table->string('last_name'); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | DB::connection()->getSchemaBuilder()->dropIfExists('profiles'); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/CommandTest.php: -------------------------------------------------------------------------------- 1 | sanitizePath( 40 | str_replace( 41 | '/public', 42 | $this->sanitizePath($this->generateFilePathFromNamespace($repositoryNamespace ?? config('model-repository.repositories_namespace'))), 43 | public_path() 44 | ) 45 | ); 46 | } 47 | 48 | 49 | /** 50 | * Reset the repository store folders and created files 51 | * 52 | * @param string $fullpath 53 | * @return void 54 | */ 55 | protected function resetRepositorysWithDirectory(string $fullpath = null) { 56 | 57 | if ( File::isDirectory($fullpath) ) { 58 | 59 | array_map('unlink', glob($fullpath . '*.*')); 60 | 61 | rmdir($fullpath); 62 | } 63 | } 64 | 65 | 66 | /** 67 | * Setup the test environment. 68 | * 69 | * @return void 70 | */ 71 | protected function setUp(): void { 72 | 73 | parent::setUp(); 74 | 75 | $this->repositoryStoreFullPath = $this->generateRepositoryClassStoreFullPath(); 76 | 77 | $self = $this; 78 | 79 | $this->beforeApplicationDestroyed(function () use ($self) { 80 | 81 | $self->resetRepositorysWithDirectory($self->repositoryStoreFullPath); 82 | }); 83 | } 84 | 85 | 86 | /** 87 | * @test 88 | */ 89 | public function repository_command_will_run() { 90 | 91 | $command = $this->artisan('make:repository UserRepository'); 92 | 93 | $command->assertExitCode(0); 94 | 95 | $command = $this->artisan('make:repository UserRepository --replace'); 96 | 97 | $command->assertExitCode(0); 98 | 99 | $command = $this->artisan('make:repository UserRepository --replace --model=App\\Models\\User'); 100 | 101 | $command->assertExitCode(0); 102 | } 103 | 104 | 105 | /** 106 | * @test 107 | */ 108 | public function repository_command_will_fail_if_repository_class_not_give() { 109 | 110 | $this->expectException(RuntimeException::class); 111 | 112 | $this->artisan('make:repository'); 113 | } 114 | 115 | 116 | public function it_will_failed_if_class_already_exists_and_not_instruct_to_replace() { 117 | 118 | $this->artisan('make:repository', ['class' => 'ProfileRepository'])->assertExitCode(0);; 119 | 120 | $this->artisan('make:repository', ['class' => 'ProfileRepository'])->assertExitCode(1);; 121 | } 122 | 123 | 124 | /** 125 | * @test 126 | */ 127 | public function it_will_generate_proper_repository_class_at_given_path() { 128 | 129 | $this->artisan('make:repository', ['class' => 'ProfileRepository'])->assertExitCode(0); 130 | 131 | $this->assertTrue(File::exists($this->repositoryStoreFullPath . 'ProfileRepository.php')); 132 | } 133 | 134 | 135 | /** 136 | * @test 137 | */ 138 | public function it_will_generate_repository_class_with_proper_content() { 139 | 140 | $this->artisan('make:repository TestRepository --replace --model=Test')->assertExitCode(0); 141 | 142 | $this->assertEquals( 143 | File::get($this->repositoryStoreFullPath . 'TestRepository.php'), 144 | File::get(__DIR__ . '/App/Repositories/TestRepository.php'), 145 | ); 146 | } 147 | 148 | 149 | /** 150 | * @test 151 | */ 152 | public function it_will_generate_repository_class_with_custom_namespace_given() { 153 | 154 | $this->artisan( 155 | 'make:repository', 156 | ['class' => 'App\\DummyRepositories\\TestRepository', '--model' => 'Test', '--replace' => true] 157 | )->assertExitCode(0); 158 | 159 | $storePath = $this->generateRepositoryClassStoreFullPath('App\\DummyRepositories'); 160 | 161 | $this->assertTrue(File::exists($storePath . 'TestRepository.php')); 162 | 163 | $this->resetRepositorysWithDirectory($storePath); 164 | } 165 | 166 | } -------------------------------------------------------------------------------- /tests/RepositoryTest.php: -------------------------------------------------------------------------------- 1 | 31 | */ 32 | protected $profileRepository; 33 | 34 | 35 | /** 36 | * Create test repositories 37 | * 38 | * @return void 39 | */ 40 | protected function createRepository() { 41 | 42 | $this->profileRepository = new ProfileRepository(new Profile); 43 | } 44 | 45 | 46 | /** 47 | * Define environment setup. 48 | * 49 | * @param Illuminate\Foundation\Application $app 50 | * @return void 51 | */ 52 | protected function defineEnvironment($app) { 53 | 54 | // Setup default database to use sqlite :memory: 55 | $app['config']->set('database.default', 'testbench'); 56 | $app['config']->set('database.connections.testbench', [ 57 | 'driver' => 'sqlite', 58 | 'database' => ':memory:', 59 | 'prefix' => '', 60 | ]); 61 | 62 | $app['config']->set('app.url', 'http://localhost/'); 63 | $app['config']->set('app.debug', false); 64 | $app['config']->set('app.key', env('APP_KEY', '1234567890123456')); 65 | $app['config']->set('app.cipher', 'AES-128-CBC'); 66 | } 67 | 68 | 69 | /** 70 | * Define database migrations. 71 | * 72 | * @return void 73 | */ 74 | protected function defineDatabaseMigrations() { 75 | 76 | $this->loadMigrationsFrom(__DIR__ . '/App/database/migrations'); 77 | 78 | $this->artisan('migrate', ['--database' => 'testbench'])->run(); 79 | 80 | $this->beforeApplicationDestroyed(function () { 81 | $this->artisan('migrate:rollback', ['--database' => 'testbench'])->run(); 82 | }); 83 | } 84 | 85 | 86 | /** 87 | * Setup the test environment. 88 | * 89 | * @return void 90 | */ 91 | protected function setUp(): void { 92 | 93 | // Code before application created. 94 | 95 | parent::setUp(); 96 | 97 | // Code after application created. 98 | 99 | $this->createRepository(); 100 | } 101 | 102 | 103 | /** 104 | * @test 105 | */ 106 | public function will_have_proper_repository_instance() { 107 | 108 | $this->assertTrue($this->profileRepository instanceof ProfileRepository); 109 | $this->assertTrue($this->profileRepository instanceof BaseRepository); 110 | $this->assertTrue($this->profileRepository instanceof RepositoryContract); 111 | } 112 | 113 | 114 | /** 115 | * @test 116 | */ 117 | public function can_initiate_repository_via_static_method() { 118 | 119 | $alternateProfileRepository = ProfileRepository::withModel(new Profile); 120 | 121 | $this->assertTrue($alternateProfileRepository instanceof ProfileRepository); 122 | $this->assertTrue($alternateProfileRepository instanceof BaseRepository); 123 | $this->assertTrue($alternateProfileRepository instanceof RepositoryContract); 124 | } 125 | 126 | 127 | /** 128 | * @test 129 | */ 130 | public function repository_class_can_return_back_proper_model_instance() { 131 | 132 | $this->assertTrue($this->profileRepository->getModel() instanceof Profile); 133 | $this->assertTrue($this->profileRepository->getModel() instanceof Model); 134 | } 135 | 136 | 137 | /** 138 | * @test 139 | */ 140 | public function repository_class_can_return_back_proper_model_class() { 141 | 142 | $this->assertEquals($this->profileRepository->getModelClass(), Profile::class); 143 | } 144 | 145 | 146 | /** 147 | * @test 148 | */ 149 | public function repository_class_can_check_if_model_has_soft_delete_feature_enabled() { 150 | 151 | $this->assertTrue($this->profileRepository->hasSoftDelete()); 152 | } 153 | 154 | 155 | /** 156 | * @test 157 | */ 158 | public function repository_can_store() { 159 | 160 | $profile = $this->profileRepository->create([ 161 | 'first_name' => 'First_Name', 162 | 'last_name' => 'Last_name' 163 | ]); 164 | 165 | $this->assertDatabaseHas('profiles', [ 166 | 'first_name' => 'First_Name', 167 | 'last_name' => 'Last_name' 168 | ]); 169 | } 170 | 171 | 172 | /** 173 | * @test 174 | */ 175 | public function repository_can_store_on_given_non_associated_table_columns() { 176 | 177 | $profile = $this->profileRepository->create([ 178 | 'first_name' => 'First_Name', 179 | 'last_name' => 'Last_name', 180 | 'some_column' => 'Some data' 181 | ]); 182 | 183 | $this->assertDatabaseHas('profiles', [ 184 | 'first_name' => 'First_Name', 185 | 'last_name' => 'Last_name' 186 | ]); 187 | } 188 | 189 | 190 | /** 191 | * @test 192 | */ 193 | public function repository_can_update() { 194 | 195 | $profile = $this->profileRepository->create([ 196 | 'first_name' => 'First_Name', 197 | 'last_name' => 'Last_name' 198 | ]); 199 | 200 | $this->profileRepository->update([ 201 | 'first_name' => 'New_First_Name', 202 | ], $profile); 203 | 204 | $this->assertDatabaseHas('profiles', [ 205 | 'first_name' => 'New_First_Name', 206 | 'last_name' => 'Last_name' 207 | ]); 208 | } 209 | 210 | 211 | /** 212 | * @test 213 | */ 214 | public function repository_can_find() { 215 | 216 | $profile = $this->profileRepository->create([ 217 | 'first_name' => 'name', 218 | 'last_name' => 'Name' 219 | ]); 220 | 221 | $this->assertEquals($this->profileRepository->find($profile->id)->id, $profile->id); 222 | $this->assertEquals($this->profileRepository->find(['first_name' => 'name'])->id, $profile->id); 223 | } 224 | 225 | /** 226 | * @test 227 | */ 228 | public function repository_can_find_multiple_as_collection() { 229 | 230 | $profile1 = $this->profileRepository->create([ 231 | 'first_name' => 'name1', 232 | 'last_name' => 'Name1' 233 | ]); 234 | 235 | $profile2 = $this->profileRepository->create([ 236 | 'first_name' => 'name2', 237 | 'last_name' => 'Name2' 238 | ]); 239 | 240 | $this->assertEquals($this->profileRepository->find([$profile1->id, $profile2->id])->count(), 2); 241 | $this->assertTrue($this->profileRepository->find([$profile1->id, $profile2->id]) instanceof Collection); 242 | } 243 | 244 | 245 | /** 246 | * @test 247 | */ 248 | public function repository_can_find_via_array_constrain() { 249 | 250 | $profile = $this->profileRepository->create([ 251 | 'first_name' => 'First_Name', 252 | 'last_name' => 'Last_name' 253 | ]); 254 | 255 | $this->assertEquals($this->profileRepository->find(['first_name' => 'First_Name'])->id, $profile->id); 256 | $this->assertEquals($this->profileRepository->find(['first_name' => 'First_Name', 'last_name' => 'Last_name'])->id, $profile->id); 257 | } 258 | 259 | 260 | /** 261 | * @test 262 | */ 263 | public function repository_can_throw_exception_on_failed_find() { 264 | 265 | $this->profileRepository->create([ 266 | 'first_name' => 'First_Name', 267 | 'last_name' => 'Last_name' 268 | ]); 269 | 270 | $this->withoutExceptionHandling(); 271 | 272 | $this->expectException(ModelNotFoundException::class); 273 | 274 | $this->profileRepository->find(100001, [], true); 275 | 276 | $this->profileRepository->find(['first_name' => 'some name'], [], true); 277 | } 278 | 279 | 280 | /** 281 | * @test 282 | */ 283 | public function repository_can_delete() { 284 | 285 | $profile = $this->profileRepository->create([ 286 | 'first_name' => 'name', 287 | 'last_name' => 'Name' 288 | ]); 289 | 290 | $this->profileRepository->delete($profile); 291 | 292 | $this->assertNull($this->profileRepository->find($profile->id)); 293 | $this->assertEquals($this->profileRepository->getModel()->onlyTrashed()->find($profile->id)->id, $profile->id); 294 | } 295 | 296 | 297 | /** 298 | * @test 299 | */ 300 | public function repository_can_force_delete() { 301 | 302 | $profile = $this->profileRepository->create([ 303 | 'first_name' => 'name', 304 | 'last_name' => 'Name' 305 | ]); 306 | 307 | $this->profileRepository->forceDelete($profile); 308 | 309 | $this->assertNull($this->profileRepository->find($profile->id)); 310 | $this->assertNull($this->profileRepository->getModel()->onlyTrashed()->find($profile->id)); 311 | } 312 | 313 | 314 | /** 315 | * @test 316 | */ 317 | public function repository_can_restore() { 318 | 319 | $profile = $this->profileRepository->create([ 320 | 'first_name' => 'name', 321 | 'last_name' => 'Name' 322 | ]); 323 | 324 | $this->profileRepository->delete($profile); 325 | 326 | $this->assertNull($this->profileRepository->find($profile->id)); 327 | 328 | $this->profileRepository->restore($profile); 329 | 330 | $this->assertEquals($this->profileRepository->find($profile->id)->id, $profile->id); 331 | } 332 | 333 | } -------------------------------------------------------------------------------- /tests/Traits/FileHelpers.php: -------------------------------------------------------------------------------- 1 | $namespace 24 | * @return mixed 25 | */ 26 | protected function generateFilePathFromNamespace(string $namespace = null) { 27 | 28 | if ( ! $namespace ) { 29 | 30 | return null; 31 | } 32 | 33 | $namespaceSegments = explode('\\', $namespace); 34 | 35 | return '/' . implode('/', array_values(array_filter($namespaceSegments))); 36 | } 37 | } -------------------------------------------------------------------------------- /tests/Traits/LaravelSetup.php: -------------------------------------------------------------------------------- 1 | set('database.default', 'testbench'); 20 | $app['config']->set('database.connections.testbench', [ 21 | 'driver' => 'sqlite', 22 | 'database' => ':memory:', 23 | 'prefix' => '', 24 | ]); 25 | 26 | $app['config']->set('app.url', 'http://localhost/'); 27 | $app['config']->set('app.debug', false); 28 | $app['config']->set('app.key', env('APP_KEY', '1234567890123456')); 29 | $app['config']->set('app.cipher', 'AES-128-CBC'); 30 | } 31 | 32 | 33 | /** 34 | * Define database migrations. 35 | * 36 | * @return void 37 | */ 38 | protected function defineDatabaseMigrations() { 39 | 40 | $this->loadMigrationsFrom(__DIR__ . '/App/database/migrations'); 41 | 42 | $this->artisan('migrate', ['--database' => 'testbench'])->run(); 43 | 44 | $this->beforeApplicationDestroyed(function () { 45 | $this->artisan('migrate:rollback', ['--database' => 'testbench'])->run(); 46 | }); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /tests/Traits/LaravelTestBootstrapping.php: -------------------------------------------------------------------------------- 1 | ModelRepository::class, 34 | ]; 35 | } 36 | } -------------------------------------------------------------------------------- /tests/config/model-repository.php: -------------------------------------------------------------------------------- 1 | \Touhidurabir\ModelRepository\BaseRepository::class, 16 | 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default model namespace prefix 21 | |-------------------------------------------------------------------------- 22 | | 23 | | The base model path which will be used to get the full namespace of the 24 | | give model for which the repository class will be genrated . 25 | | 26 | */ 27 | 28 | 'models_namespace' => 'App\\Models', 29 | 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Path/location namespace to save repository classes 34 | |-------------------------------------------------------------------------- 35 | | 36 | | location where to store the repository classes to save/store. 37 | | 38 | */ 39 | 40 | 'repositories_namespace' => 'App\\Repositories', 41 | 42 | ]; --------------------------------------------------------------------------------