├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── composer.lock └── src ├── Abstracts ├── CriteriaAbstract.php ├── RepositoryAbstract.php └── ServiceAbstract.php ├── Console └── Commands │ ├── RepositoryCommand.php │ └── Stubs │ ├── RepositoryStub.stub │ └── ServicesStub.stub ├── Contracts ├── CriteriaContract.php ├── RepositoryContract.php └── ServiceContract.php ├── Criterias └── FindUsingLikeCriteria.php ├── Exceptions └── RepositoryException.php └── Providers └── RepositoryProvider.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.5 4 | - 5.6 5 | - hhvm 6 | - nightly 7 | install: 8 | - composer require andersonef/repositories-pattern 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Repositories Pattern on Laravel 5.1 or superior 2 | 3 | This package allows you to implement the Repositories design pattern in your laravel application. It makes easy to use a service layer too. 4 | 5 | ##Instalation 6 | Its simply to install, just run: 7 | 8 | composer require andersonef/repositories-pattern 9 | 10 | 11 | ##Service Provider 12 | You must register this package service provider at you **config/app.php** file. Just add this line at your $providers array: 13 | 14 | ```php 15 | Andersonef\Repositories\Providers\RepositoryProvider::class, 16 | ``` 17 | 18 | ##Creating your Repositories and Services: 19 | At your console, enter the following command: 20 | 21 | php artisan make:repository BlogNamespace/Post --entity=App/Models/Post 22 | 23 | Remember to use the complete namespace to your entity path. If your are using a custom namespace, use: 24 | 25 | php artisan make:repository BlogNamespace/Post --entity=CustomNamespace/Models/Post 26 | 27 | 28 | This will create the following file structure in your app directory: 29 | 30 | app/ 31 | 32 | Repositories/ 33 | 34 | BlogNamespace/ 35 | 36 | PostRepository.php 37 | 38 | Services/ 39 | 40 | BlogNamespace/ 41 | 42 | PostService.php 43 | 44 | 45 | ##Repository file structure 46 | Your repository file will be created with the following code: 47 | 48 | ```php 49 | namespace Inet\Repositories\BlogNamespace; 50 | 51 | use Andersonef\Repositories\Abstracts\RepositoryAbstract; 52 | use \Post; 53 | 54 | /** 55 | * Data repository to work with entity Post. 56 | * 57 | * Class PostRepository 58 | * @package Inet\Repositories\BlogNamespace 59 | */ 60 | class PostRepository extends RepositoryAbstract{ 61 | 62 | 63 | public function entity() 64 | { 65 | return \Post::class; 66 | } 67 | 68 | } 69 | ``` 70 | 71 | ##Service file structure 72 | And your PostService.php file wil be created with the following code: 73 | 74 | ```php 75 | namespace Inet\Services\BlogNamespace; 76 | 77 | use Andersonef\Repositories\Abstracts\ServiceAbstract; 78 | use Illuminate\Database\DatabaseManager; 79 | use \Inet\Repositories\BlogNamespace\PostRepository; 80 | 81 | /** 82 | * Service layer that will applies all application rules to work with Post class. 83 | * 84 | * Class PostService 85 | * @package Inet\Services\BlogNamespace 86 | */ 87 | class PostService extends ServiceAbstract{ 88 | 89 | /** 90 | * This constructor will receive by dependency injection a instance of PostRepository and DatabaseManager. 91 | * 92 | * @param PostRepository $repository 93 | * @param DatabaseManager $db 94 | */ 95 | public function __construct(PostRepository $repository, DatabaseManager $db) 96 | { 97 | parent::__construct($repository, $db); 98 | } 99 | } 100 | ``` 101 | 102 | ##Usage 103 | Using this pattern you wil be able to separate your application rules from your data access rules and you will be able to reuse your code in a very simple way. 104 | Imagine that you have an app that users can register using the public pages AND the admin register users using the admin panel. The rules to register an user are the same on both cases, but the admin panel must require an admin logged user AND has an option to isent the new user to pay his subscription value. 105 | In the old way you would have to replicate the code or use a laravel command to isolate the user registration rules. 106 | Using the service layer and the repository pattern you will write the user registration rules inside the UserService and on both controllers (public page registration and admin panel) you would call the $userService->create($request->all()). 107 | If you must implement an api to save users from an android interface, you can reuse your service layer and just change the way your controller respond to the client. 108 | 109 | ##Repository Inherited Methods: 110 | Your repository have some inherited methods from RepositoryAbstract class. They are: 111 | - **create(array $data);**: Tries to create a new instance of your specified entity. **WARNING: Your entity must declare the $fillable field** 112 | - **update(array $data, $id);**: Update specified entity. 113 | - **delete($id);**: Delete the specified entity 114 | - **find($id, array $columns = ['*']);**: Find an instance of specified entity by id 115 | - **findBy(array $fields, array $columns = ['*']);**: Find a collection of specified instances using the fields (see phpdocs). 116 | 117 | ##Service Inherited Methods: 118 | Your service will be similar to your repository. The main difference is that your service layer must implement application logic, so it have transaction in its methods: 119 | - **create(array $data)**: Open a transaction and try to create an instance using the $data array. You can override it 120 | - **update(array $data, $id)**: Open a transaction and try to update the instance using $data array. 121 | - **delete($id)**: OPen a transaction and try to delete an instance of specified entity. 122 | 123 | ##Magic Methods: 124 | For convenience, we can use magic methods on both service and repository classes: 125 | 126 | ```php 127 | $yourservice->repositoryMethod(); // this will be the same as: $yourservice->getRepository()->repositoryMethod(); 128 | $yourRepository->entityMethod(); // this will be the same as: $tyourRepository->getEntity()->entityMethod(); 129 | ``` 130 | 131 | ##Using Criterias: 132 | You can implement criteria to reuse your application query rules. This package brings you one default criteria, the **FindUsingLikeCriteria**. 133 | Lets think you must implement a search field on your blog, and must bring all your posts that have some text like $query variable. 134 | You can simply do: 135 | 136 | ```php 137 | $result = $postService->findByCriteria(new FindUsingLikeCriteria($request->get('textQuery')))->paginate(10); 138 | ``` 139 | 140 | This will returns to you a collection of posts that has title, or content, or author like the text inside 'textQuery' request attribute. 141 | You can create your own criterias, its really simple to do it: 142 | ##Creating your own criteria 143 | This wil be your custom criteria: 144 | 145 | ```php 146 | namespace Andersonef\Repositories\Criteria; 147 | 148 | use Andersonef\Repositories\Abstracts\CriteriaAbstract; 149 | use Andersonef\Repositories\Contracts\RepositoryContract; 150 | use Illuminate\Database\Eloquent\Model; 151 | 152 | class UnreadRecentPostsCriteria extends CriteriaAbstract{ 153 | 154 | public function apply(Model $model, RepositoryContract $repository) 155 | { 156 | $model 157 | ->where('created_at','>',(new \DateTime())->sub(new \DateInterval('P3D'))->format('Y-m-d')) 158 | ->where('status_read', '=', 1); 159 | return $model; 160 | } 161 | } 162 | ``` 163 | 164 | ##Credits 165 | This package has been created based on this other: https://github.com/prettus/l5-repository 166 | I just implement a few more options and make some fews changes to turn it better for the company I work. 167 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "andersonef/repositories-pattern", 3 | "description": "Laravel Repositories Pattern Implementation", 4 | "keywords": [ 5 | "laravel", 6 | "repository", 7 | "repositories", 8 | "eloquent", 9 | "database" 10 | ], 11 | "licence": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Anderson Nunes", 15 | "email": "anderson.nuneseth@gmail.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=5.6.0", 20 | "illuminate/database":">=5.1", 21 | "illuminate/support":">=5.1", 22 | "illuminate/console": ">=5.1", 23 | "illuminate/filesystem": ">=5.1", 24 | "symfony/class-loader": ">=2.3" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Andersonef\\Repositories\\": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Andersonef\\Tests\\Repositories\\": "tests/" 34 | } 35 | }, 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.0-dev" 39 | } 40 | }, 41 | "minimum-stability": "dev", 42 | "prefer-stable": true 43 | 44 | 45 | } -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "25ba6e584f1cd15e47718c819da6fbe4", 8 | "packages": [ 9 | { 10 | "name": "danielstjules/stringy", 11 | "version": "1.9.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/danielstjules/Stringy.git", 15 | "reference": "3cf18e9e424a6dedc38b7eb7ef580edb0929461b" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/danielstjules/Stringy/zipball/3cf18e9e424a6dedc38b7eb7ef580edb0929461b", 20 | "reference": "3cf18e9e424a6dedc38b7eb7ef580edb0929461b", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-mbstring": "*", 25 | "php": ">=5.3.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "~4.0" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-4": { 33 | "Stringy\\": "src/" 34 | }, 35 | "files": [ 36 | "src/Create.php" 37 | ] 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Daniel St. Jules", 46 | "email": "danielst.jules@gmail.com", 47 | "homepage": "http://www.danielstjules.com" 48 | } 49 | ], 50 | "description": "A string manipulation library with multibyte support", 51 | "homepage": "https://github.com/danielstjules/Stringy", 52 | "keywords": [ 53 | "UTF", 54 | "helpers", 55 | "manipulation", 56 | "methods", 57 | "multibyte", 58 | "string", 59 | "utf-8", 60 | "utility", 61 | "utils" 62 | ], 63 | "time": "2015-02-10 06:19:18" 64 | }, 65 | { 66 | "name": "doctrine/inflector", 67 | "version": "v1.0.1", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/doctrine/inflector.git", 71 | "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/0bcb2e79d8571787f18b7eb036ed3d004908e604", 76 | "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": ">=5.3.2" 81 | }, 82 | "require-dev": { 83 | "phpunit/phpunit": "4.*" 84 | }, 85 | "type": "library", 86 | "extra": { 87 | "branch-alias": { 88 | "dev-master": "1.0.x-dev" 89 | } 90 | }, 91 | "autoload": { 92 | "psr-0": { 93 | "Doctrine\\Common\\Inflector\\": "lib/" 94 | } 95 | }, 96 | "notification-url": "https://packagist.org/downloads/", 97 | "license": [ 98 | "MIT" 99 | ], 100 | "authors": [ 101 | { 102 | "name": "Roman Borschel", 103 | "email": "roman@code-factory.org" 104 | }, 105 | { 106 | "name": "Benjamin Eberlei", 107 | "email": "kontakt@beberlei.de" 108 | }, 109 | { 110 | "name": "Guilherme Blanco", 111 | "email": "guilhermeblanco@gmail.com" 112 | }, 113 | { 114 | "name": "Jonathan Wage", 115 | "email": "jonwage@gmail.com" 116 | }, 117 | { 118 | "name": "Johannes Schmitt", 119 | "email": "schmittjoh@gmail.com" 120 | } 121 | ], 122 | "description": "Common String Manipulations with regard to casing and singular/plural rules.", 123 | "homepage": "http://www.doctrine-project.org", 124 | "keywords": [ 125 | "inflection", 126 | "pluralize", 127 | "singularize", 128 | "string" 129 | ], 130 | "time": "2014-12-20 21:24:13" 131 | }, 132 | { 133 | "name": "illuminate/console", 134 | "version": "v5.1.6", 135 | "source": { 136 | "type": "git", 137 | "url": "https://github.com/illuminate/console.git", 138 | "reference": "dac9a3584fb113b4476b16bfe9b363da5bb5cac6" 139 | }, 140 | "dist": { 141 | "type": "zip", 142 | "url": "https://api.github.com/repos/illuminate/console/zipball/dac9a3584fb113b4476b16bfe9b363da5bb5cac6", 143 | "reference": "dac9a3584fb113b4476b16bfe9b363da5bb5cac6", 144 | "shasum": "" 145 | }, 146 | "require": { 147 | "illuminate/contracts": "5.1.*", 148 | "illuminate/support": "5.1.*", 149 | "nesbot/carbon": "~1.19", 150 | "php": ">=5.5.9", 151 | "symfony/console": "2.7.*" 152 | }, 153 | "suggest": { 154 | "guzzlehttp/guzzle": "Required to use the thenPing method on schedules (~5.3|~6.0).", 155 | "mtdowling/cron-expression": "Required to use scheduling component (~1.0).", 156 | "symfony/process": "Required to use scheduling component (2.7.*)." 157 | }, 158 | "type": "library", 159 | "extra": { 160 | "branch-alias": { 161 | "dev-master": "5.1-dev" 162 | } 163 | }, 164 | "autoload": { 165 | "psr-4": { 166 | "Illuminate\\Console\\": "" 167 | } 168 | }, 169 | "notification-url": "https://packagist.org/downloads/", 170 | "license": [ 171 | "MIT" 172 | ], 173 | "authors": [ 174 | { 175 | "name": "Taylor Otwell", 176 | "email": "taylorotwell@gmail.com" 177 | } 178 | ], 179 | "description": "The Illuminate Console package.", 180 | "homepage": "http://laravel.com", 181 | "time": "2015-07-02 03:09:16" 182 | }, 183 | { 184 | "name": "illuminate/container", 185 | "version": "v5.1.6", 186 | "source": { 187 | "type": "git", 188 | "url": "https://github.com/illuminate/container.git", 189 | "reference": "248cd26c38d1e29eac74d32d9fff158b2232a3c6" 190 | }, 191 | "dist": { 192 | "type": "zip", 193 | "url": "https://api.github.com/repos/illuminate/container/zipball/248cd26c38d1e29eac74d32d9fff158b2232a3c6", 194 | "reference": "248cd26c38d1e29eac74d32d9fff158b2232a3c6", 195 | "shasum": "" 196 | }, 197 | "require": { 198 | "illuminate/contracts": "5.1.*", 199 | "php": ">=5.5.9" 200 | }, 201 | "type": "library", 202 | "extra": { 203 | "branch-alias": { 204 | "dev-master": "5.1-dev" 205 | } 206 | }, 207 | "autoload": { 208 | "psr-4": { 209 | "Illuminate\\Container\\": "" 210 | } 211 | }, 212 | "notification-url": "https://packagist.org/downloads/", 213 | "license": [ 214 | "MIT" 215 | ], 216 | "authors": [ 217 | { 218 | "name": "Taylor Otwell", 219 | "email": "taylorotwell@gmail.com" 220 | } 221 | ], 222 | "description": "The Illuminate Container package.", 223 | "homepage": "http://laravel.com", 224 | "time": "2015-06-13 00:40:15" 225 | }, 226 | { 227 | "name": "illuminate/contracts", 228 | "version": "v5.1.1", 229 | "source": { 230 | "type": "git", 231 | "url": "https://github.com/illuminate/contracts.git", 232 | "reference": "6e18848e5d332d802aa8bfa60b95e022023973f1" 233 | }, 234 | "dist": { 235 | "type": "zip", 236 | "url": "https://api.github.com/repos/illuminate/contracts/zipball/6e18848e5d332d802aa8bfa60b95e022023973f1", 237 | "reference": "6e18848e5d332d802aa8bfa60b95e022023973f1", 238 | "shasum": "" 239 | }, 240 | "require": { 241 | "php": ">=5.5.9" 242 | }, 243 | "type": "library", 244 | "extra": { 245 | "branch-alias": { 246 | "dev-master": "5.1-dev" 247 | } 248 | }, 249 | "autoload": { 250 | "psr-4": { 251 | "Illuminate\\Contracts\\": "" 252 | } 253 | }, 254 | "notification-url": "https://packagist.org/downloads/", 255 | "license": [ 256 | "MIT" 257 | ], 258 | "authors": [ 259 | { 260 | "name": "Taylor Otwell", 261 | "email": "taylorotwell@gmail.com" 262 | } 263 | ], 264 | "description": "The Illuminate Contracts package.", 265 | "homepage": "http://laravel.com", 266 | "time": "2015-06-08 18:13:21" 267 | }, 268 | { 269 | "name": "illuminate/database", 270 | "version": "v5.1.6", 271 | "source": { 272 | "type": "git", 273 | "url": "https://github.com/illuminate/database.git", 274 | "reference": "0aca69fe48c1253451a5fa286082fa99814591b6" 275 | }, 276 | "dist": { 277 | "type": "zip", 278 | "url": "https://api.github.com/repos/illuminate/database/zipball/0aca69fe48c1253451a5fa286082fa99814591b6", 279 | "reference": "0aca69fe48c1253451a5fa286082fa99814591b6", 280 | "shasum": "" 281 | }, 282 | "require": { 283 | "illuminate/container": "5.1.*", 284 | "illuminate/contracts": "5.1.*", 285 | "illuminate/support": "5.1.*", 286 | "nesbot/carbon": "~1.19", 287 | "php": ">=5.5.9" 288 | }, 289 | "suggest": { 290 | "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).", 291 | "fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).", 292 | "illuminate/console": "Required to use the database commands (5.1.*).", 293 | "illuminate/events": "Required to use the observers with Eloquent (5.1.*).", 294 | "illuminate/filesystem": "Required to use the migrations (5.1.*)." 295 | }, 296 | "type": "library", 297 | "extra": { 298 | "branch-alias": { 299 | "dev-master": "5.1-dev" 300 | } 301 | }, 302 | "autoload": { 303 | "psr-4": { 304 | "Illuminate\\Database\\": "" 305 | } 306 | }, 307 | "notification-url": "https://packagist.org/downloads/", 308 | "license": [ 309 | "MIT" 310 | ], 311 | "authors": [ 312 | { 313 | "name": "Taylor Otwell", 314 | "email": "taylorotwell@gmail.com" 315 | } 316 | ], 317 | "description": "The Illuminate Database package.", 318 | "homepage": "http://laravel.com", 319 | "keywords": [ 320 | "database", 321 | "laravel", 322 | "orm", 323 | "sql" 324 | ], 325 | "time": "2015-07-03 08:31:13" 326 | }, 327 | { 328 | "name": "illuminate/filesystem", 329 | "version": "v5.1.6", 330 | "source": { 331 | "type": "git", 332 | "url": "https://github.com/illuminate/filesystem.git", 333 | "reference": "f57b8fdb0e2d576ae10fbf7c1b2d2f040abdc814" 334 | }, 335 | "dist": { 336 | "type": "zip", 337 | "url": "https://api.github.com/repos/illuminate/filesystem/zipball/f57b8fdb0e2d576ae10fbf7c1b2d2f040abdc814", 338 | "reference": "f57b8fdb0e2d576ae10fbf7c1b2d2f040abdc814", 339 | "shasum": "" 340 | }, 341 | "require": { 342 | "illuminate/contracts": "5.1.*", 343 | "illuminate/support": "5.1.*", 344 | "php": ">=5.5.9", 345 | "symfony/finder": "2.7.*" 346 | }, 347 | "suggest": { 348 | "league/flysystem": "Required to use the Flysystem local and FTP drivers (~1.0).", 349 | "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", 350 | "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0)." 351 | }, 352 | "type": "library", 353 | "extra": { 354 | "branch-alias": { 355 | "dev-master": "5.1-dev" 356 | } 357 | }, 358 | "autoload": { 359 | "psr-4": { 360 | "Illuminate\\Filesystem\\": "" 361 | } 362 | }, 363 | "notification-url": "https://packagist.org/downloads/", 364 | "license": [ 365 | "MIT" 366 | ], 367 | "authors": [ 368 | { 369 | "name": "Taylor Otwell", 370 | "email": "taylorotwell@gmail.com" 371 | } 372 | ], 373 | "description": "The Illuminate Filesystem package.", 374 | "homepage": "http://laravel.com", 375 | "time": "2015-06-18 02:16:31" 376 | }, 377 | { 378 | "name": "illuminate/support", 379 | "version": "v5.1.6", 380 | "source": { 381 | "type": "git", 382 | "url": "https://github.com/illuminate/support.git", 383 | "reference": "9c6e35802e6e7c807170a0ee263d1bfcad6cbab5" 384 | }, 385 | "dist": { 386 | "type": "zip", 387 | "url": "https://api.github.com/repos/illuminate/support/zipball/9c6e35802e6e7c807170a0ee263d1bfcad6cbab5", 388 | "reference": "9c6e35802e6e7c807170a0ee263d1bfcad6cbab5", 389 | "shasum": "" 390 | }, 391 | "require": { 392 | "danielstjules/stringy": "~1.8", 393 | "doctrine/inflector": "~1.0", 394 | "ext-mbstring": "*", 395 | "illuminate/contracts": "5.1.*", 396 | "php": ">=5.5.9" 397 | }, 398 | "suggest": { 399 | "jeremeamia/superclosure": "Required to be able to serialize closures (~2.0).", 400 | "symfony/var-dumper": "Required to use the dd function (2.7.*)." 401 | }, 402 | "type": "library", 403 | "extra": { 404 | "branch-alias": { 405 | "dev-master": "5.1-dev" 406 | } 407 | }, 408 | "autoload": { 409 | "psr-4": { 410 | "Illuminate\\Support\\": "" 411 | }, 412 | "files": [ 413 | "helpers.php" 414 | ] 415 | }, 416 | "notification-url": "https://packagist.org/downloads/", 417 | "license": [ 418 | "MIT" 419 | ], 420 | "authors": [ 421 | { 422 | "name": "Taylor Otwell", 423 | "email": "taylorotwell@gmail.com" 424 | } 425 | ], 426 | "description": "The Illuminate Support package.", 427 | "homepage": "http://laravel.com", 428 | "time": "2015-07-02 02:30:30" 429 | }, 430 | { 431 | "name": "nesbot/carbon", 432 | "version": "1.20.0", 433 | "source": { 434 | "type": "git", 435 | "url": "https://github.com/briannesbitt/Carbon.git", 436 | "reference": "bfd3eaba109c9a2405c92174c8e17f20c2b9caf3" 437 | }, 438 | "dist": { 439 | "type": "zip", 440 | "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bfd3eaba109c9a2405c92174c8e17f20c2b9caf3", 441 | "reference": "bfd3eaba109c9a2405c92174c8e17f20c2b9caf3", 442 | "shasum": "" 443 | }, 444 | "require": { 445 | "php": ">=5.3.0", 446 | "symfony/translation": "~2.6|~3.0" 447 | }, 448 | "require-dev": { 449 | "phpunit/phpunit": "~4.0" 450 | }, 451 | "type": "library", 452 | "autoload": { 453 | "psr-0": { 454 | "Carbon": "src" 455 | } 456 | }, 457 | "notification-url": "https://packagist.org/downloads/", 458 | "license": [ 459 | "MIT" 460 | ], 461 | "authors": [ 462 | { 463 | "name": "Brian Nesbitt", 464 | "email": "brian@nesbot.com", 465 | "homepage": "http://nesbot.com" 466 | } 467 | ], 468 | "description": "A simple API extension for DateTime.", 469 | "homepage": "http://carbon.nesbot.com", 470 | "keywords": [ 471 | "date", 472 | "datetime", 473 | "time" 474 | ], 475 | "time": "2015-06-25 04:19:39" 476 | }, 477 | { 478 | "name": "symfony/class-loader", 479 | "version": "v2.7.1", 480 | "source": { 481 | "type": "git", 482 | "url": "https://github.com/symfony/ClassLoader.git", 483 | "reference": "84843730de48ca0feba91004a03c7c952f8ea1da" 484 | }, 485 | "dist": { 486 | "type": "zip", 487 | "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/84843730de48ca0feba91004a03c7c952f8ea1da", 488 | "reference": "84843730de48ca0feba91004a03c7c952f8ea1da", 489 | "shasum": "" 490 | }, 491 | "require": { 492 | "php": ">=5.3.9" 493 | }, 494 | "require-dev": { 495 | "symfony/finder": "~2.0,>=2.0.5", 496 | "symfony/phpunit-bridge": "~2.7" 497 | }, 498 | "type": "library", 499 | "extra": { 500 | "branch-alias": { 501 | "dev-master": "2.7-dev" 502 | } 503 | }, 504 | "autoload": { 505 | "psr-4": { 506 | "Symfony\\Component\\ClassLoader\\": "" 507 | } 508 | }, 509 | "notification-url": "https://packagist.org/downloads/", 510 | "license": [ 511 | "MIT" 512 | ], 513 | "authors": [ 514 | { 515 | "name": "Fabien Potencier", 516 | "email": "fabien@symfony.com" 517 | }, 518 | { 519 | "name": "Symfony Community", 520 | "homepage": "https://symfony.com/contributors" 521 | } 522 | ], 523 | "description": "Symfony ClassLoader Component", 524 | "homepage": "https://symfony.com", 525 | "time": "2015-06-08 09:37:21" 526 | }, 527 | { 528 | "name": "symfony/console", 529 | "version": "v2.7.1", 530 | "source": { 531 | "type": "git", 532 | "url": "https://github.com/symfony/Console.git", 533 | "reference": "564398bc1f33faf92fc2ec86859983d30eb81806" 534 | }, 535 | "dist": { 536 | "type": "zip", 537 | "url": "https://api.github.com/repos/symfony/Console/zipball/564398bc1f33faf92fc2ec86859983d30eb81806", 538 | "reference": "564398bc1f33faf92fc2ec86859983d30eb81806", 539 | "shasum": "" 540 | }, 541 | "require": { 542 | "php": ">=5.3.9" 543 | }, 544 | "require-dev": { 545 | "psr/log": "~1.0", 546 | "symfony/event-dispatcher": "~2.1", 547 | "symfony/phpunit-bridge": "~2.7", 548 | "symfony/process": "~2.1" 549 | }, 550 | "suggest": { 551 | "psr/log": "For using the console logger", 552 | "symfony/event-dispatcher": "", 553 | "symfony/process": "" 554 | }, 555 | "type": "library", 556 | "extra": { 557 | "branch-alias": { 558 | "dev-master": "2.7-dev" 559 | } 560 | }, 561 | "autoload": { 562 | "psr-4": { 563 | "Symfony\\Component\\Console\\": "" 564 | } 565 | }, 566 | "notification-url": "https://packagist.org/downloads/", 567 | "license": [ 568 | "MIT" 569 | ], 570 | "authors": [ 571 | { 572 | "name": "Fabien Potencier", 573 | "email": "fabien@symfony.com" 574 | }, 575 | { 576 | "name": "Symfony Community", 577 | "homepage": "https://symfony.com/contributors" 578 | } 579 | ], 580 | "description": "Symfony Console Component", 581 | "homepage": "https://symfony.com", 582 | "time": "2015-06-10 15:30:22" 583 | }, 584 | { 585 | "name": "symfony/finder", 586 | "version": "v2.7.1", 587 | "source": { 588 | "type": "git", 589 | "url": "https://github.com/symfony/Finder.git", 590 | "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75" 591 | }, 592 | "dist": { 593 | "type": "zip", 594 | "url": "https://api.github.com/repos/symfony/Finder/zipball/c13a40d638aeede1e8400f8c956c7f9246c05f75", 595 | "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75", 596 | "shasum": "" 597 | }, 598 | "require": { 599 | "php": ">=5.3.9" 600 | }, 601 | "require-dev": { 602 | "symfony/phpunit-bridge": "~2.7" 603 | }, 604 | "type": "library", 605 | "extra": { 606 | "branch-alias": { 607 | "dev-master": "2.7-dev" 608 | } 609 | }, 610 | "autoload": { 611 | "psr-4": { 612 | "Symfony\\Component\\Finder\\": "" 613 | } 614 | }, 615 | "notification-url": "https://packagist.org/downloads/", 616 | "license": [ 617 | "MIT" 618 | ], 619 | "authors": [ 620 | { 621 | "name": "Fabien Potencier", 622 | "email": "fabien@symfony.com" 623 | }, 624 | { 625 | "name": "Symfony Community", 626 | "homepage": "https://symfony.com/contributors" 627 | } 628 | ], 629 | "description": "Symfony Finder Component", 630 | "homepage": "https://symfony.com", 631 | "time": "2015-06-04 20:11:48" 632 | }, 633 | { 634 | "name": "symfony/translation", 635 | "version": "v2.7.1", 636 | "source": { 637 | "type": "git", 638 | "url": "https://github.com/symfony/Translation.git", 639 | "reference": "8349a2b0d11bd0311df9e8914408080912983a0b" 640 | }, 641 | "dist": { 642 | "type": "zip", 643 | "url": "https://api.github.com/repos/symfony/Translation/zipball/8349a2b0d11bd0311df9e8914408080912983a0b", 644 | "reference": "8349a2b0d11bd0311df9e8914408080912983a0b", 645 | "shasum": "" 646 | }, 647 | "require": { 648 | "php": ">=5.3.9" 649 | }, 650 | "conflict": { 651 | "symfony/config": "<2.7" 652 | }, 653 | "require-dev": { 654 | "psr/log": "~1.0", 655 | "symfony/config": "~2.7", 656 | "symfony/intl": "~2.3", 657 | "symfony/phpunit-bridge": "~2.7", 658 | "symfony/yaml": "~2.2" 659 | }, 660 | "suggest": { 661 | "psr/log": "To use logging capability in translator", 662 | "symfony/config": "", 663 | "symfony/yaml": "" 664 | }, 665 | "type": "library", 666 | "extra": { 667 | "branch-alias": { 668 | "dev-master": "2.7-dev" 669 | } 670 | }, 671 | "autoload": { 672 | "psr-4": { 673 | "Symfony\\Component\\Translation\\": "" 674 | } 675 | }, 676 | "notification-url": "https://packagist.org/downloads/", 677 | "license": [ 678 | "MIT" 679 | ], 680 | "authors": [ 681 | { 682 | "name": "Fabien Potencier", 683 | "email": "fabien@symfony.com" 684 | }, 685 | { 686 | "name": "Symfony Community", 687 | "homepage": "https://symfony.com/contributors" 688 | } 689 | ], 690 | "description": "Symfony Translation Component", 691 | "homepage": "https://symfony.com", 692 | "time": "2015-06-11 17:26:34" 693 | } 694 | ], 695 | "packages-dev": [], 696 | "aliases": [], 697 | "minimum-stability": "dev", 698 | "stability-flags": [], 699 | "prefer-stable": true, 700 | "prefer-lowest": false, 701 | "platform": { 702 | "php": ">=5.5.0" 703 | }, 704 | "platform-dev": [] 705 | } 706 | -------------------------------------------------------------------------------- /src/Abstracts/CriteriaAbstract.php: -------------------------------------------------------------------------------- 1 | makeEntity(); 33 | $this->criterias = $criterias; 34 | } 35 | 36 | /** Must return the class of entity this repository will work 37 | * @return string 38 | */ 39 | abstract function entity(); 40 | 41 | /** Initialize the entity property in this repository and check the type of it. 42 | * @throws RepositoryException when the return of entity() method doesn't returns a valid eloquent model class name. 43 | */ 44 | protected function makeEntity() 45 | { 46 | $model = app($this->entity()); 47 | if(!$model instanceof Model) 48 | throw new RepositoryException("Class {$this->entity()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); 49 | $this->entity = $model; 50 | } 51 | 52 | /** Return the entity this repository works with. 53 | * @return Model 54 | */ 55 | public function getEntity() 56 | { 57 | return $this->entity; 58 | } 59 | 60 | 61 | /** If this method receives true, so the result of your query will ignore the criterias applied to it. 62 | * @param bool $skip 63 | */ 64 | public function skipCriteria($skip = true) 65 | { 66 | $this->skipCriteria = $skip; 67 | } 68 | 69 | /** Return the collection of criterias 70 | * @return Collection 71 | */ 72 | public function getCriterias() 73 | { 74 | return $this->criterias; 75 | } 76 | 77 | /** Return a query formed by the criteria received. Usage: $postRepository->findByCriteria(new OlderPostsCriteria())->paginate(10); 78 | * @param CriteriaAbstract $criteria 79 | * @return $this 80 | */ 81 | public function findByCriteria(CriteriaAbstract $criteria) 82 | { 83 | $this->entity = $criteria->apply($this->entity, $this); 84 | return $this; 85 | } 86 | 87 | /** Insert a criteria on the stack. You can use this method as a chain. 88 | * Usage: $postRepository->pushCriteria(new OlderPostsCriteria())->pushCriteria(new UnreadPostsCriteria())->paginate(10); 89 | * @param CriteriaAbstract $criteria 90 | * @return RepositoryAbstract 91 | */ 92 | 93 | public function pushCriteria(CriteriaAbstract $criteria, $clausule = 'AND') 94 | { 95 | $this->criterias->push($criteria); 96 | return $this; 97 | } 98 | 99 | /** Applies the criterias in the stack before return the result. 100 | * @return $this 101 | */ 102 | public function applyCriteria() 103 | { 104 | if($this->skipCriteria) return $this; 105 | foreach($this->getCriterias() as $criteria) 106 | { 107 | $this->model = $criteria->apply($this->model, $this); 108 | } 109 | return $this; 110 | } 111 | 112 | 113 | /** Return all objects from database, using the criterias on the stack. 114 | * @param array $columns 115 | * @return mixed 116 | */ 117 | public function all(array $columns = ['*']) 118 | { 119 | $this->applyCriteria(); 120 | return $this->entity->get($columns); 121 | } 122 | 123 | /** Paginates all objects from database, using the criterias on the stack. 124 | * @param int $perpage 125 | * @param array $columns 126 | */ 127 | public function paginate($perpage = 15, $columns = ['*']) 128 | { 129 | $this->applyCriteria(); 130 | return $this->entity->paginate($perpage, $columns); 131 | } 132 | 133 | /** Insert a new entity on database 134 | * @param array $data 135 | * @return Model created 136 | */ 137 | public function create(array $data) 138 | { 139 | $this->entity = app($this->entity()); 140 | return $this->entity->create($data); 141 | } 142 | 143 | /** Update entity on database. 144 | * @param array $data 145 | * @param $id 146 | */ 147 | public function update(array $data, $id) 148 | { 149 | $this->entity = app($this->entity())->find($id); 150 | $this->entity->update($data); 151 | return app($this->entity())->find($id); 152 | } 153 | 154 | /** Delete the entity on database 155 | * @param $id 156 | */ 157 | public function delete($id) 158 | { 159 | $this->entity = app($this->entity()); 160 | $this->entity->destroy($id); 161 | } 162 | 163 | /** Find an entity using its primary key 164 | * @param $id.c 165 | * @param array $columns 166 | * @return Model 167 | */ 168 | public function find($id, $columns = ['*']) 169 | { 170 | return $this->entity->find($id, $columns); 171 | } 172 | 173 | /** Find a collection of entities using one or more fields and its values. 174 | * Usage: $postRepository->findBy(['author_id' => 5, 'post_status' => 1])->paginate(10); 175 | * @param $fields associative array representing the entities fields and its values 176 | * @param array $columns 177 | * @return $this 178 | */ 179 | public function findBy($fields, $columns = ['*']) 180 | { 181 | $entity = $this->entity->newQuery(); 182 | foreach($fields as $field => $value) 183 | { 184 | $entity->where($field, '=', $value); 185 | } 186 | return $entity; 187 | 188 | } 189 | 190 | 191 | /** Magic method: Use this method to call entity method directly. Instead of $repository->getEntity()->find(5), you can use: $repository->find(5). 192 | * @param $method 193 | * @param $arguments 194 | * @return mixed 195 | */ 196 | public function __call($method, $arguments) 197 | { 198 | if(method_exists($this, $method)) return call_user_func_array([$this, $method], $arguments); 199 | return call_user_func_array([$this->entity, $method], $arguments); 200 | } 201 | 202 | 203 | } -------------------------------------------------------------------------------- /src/Abstracts/ServiceAbstract.php: -------------------------------------------------------------------------------- 1 | transaction rules 22 | * Admin/ 23 | * StoreController.php 24 | * function postProcessTransaction() => some specific validations and the same rules from /Controllers/StoreController. 25 | * 26 | * You will do: 27 | * Http/ 28 | * Controllers/ 29 | * StoreController.php 30 | * function postProcessTransaction() => $this->StoreService->processTransaction(); 31 | * Admin/ 32 | * StoreController.php 33 | * function postProcessTransaction() => 34 | * //specific validations 35 | * $this->StoreService->processTransaction(); 36 | * 37 | * This way, if you need to update your app rules, you will do it only at service layer. 38 | * 39 | * In this class you can use the default methods but you can implement your custom methods or rewrite this custom methods. 40 | * For Instance: 41 | * class YourCustomService extends ServiceAbstract{ 42 | * protected $AnotherService; 43 | * 44 | * function __construct(AnotherCustomService $service){ 45 | * $this->AnotherCustomService = $service; 46 | * } 47 | * public function create(array $data){ 48 | * //custom rules 49 | * return parent::create($data); 50 | * } 51 | * 52 | * public function customMethod($param1, $param2){ 53 | * $object = parent::create($param1); 54 | * //send mail 55 | * $this->AnotherCustomService->create($param2); 56 | * } 57 | * } 58 | * Class ServiceAbstract 59 | * @package Andersonef\Repositories\Abstracts 60 | */ 61 | class ServiceAbstract implements ServiceContract { 62 | 63 | protected $Repository; 64 | protected $db; 65 | 66 | 67 | function __construct(RepositoryAbstract $ra, DatabaseManager $db) 68 | { 69 | $this->Repository = $ra; 70 | $this->db = $db; 71 | } 72 | 73 | public function create(array $data) 74 | { 75 | try{ 76 | $this->db->beginTransaction(); 77 | $entity = $this->Repository->create($data); 78 | $this->db->commit(); 79 | }catch(\Exception $e){ 80 | $this->db->rollback(); 81 | throw $e; 82 | } 83 | return $entity; 84 | } 85 | 86 | public function update(array $data, $id) 87 | { 88 | try{ 89 | $this->db->beginTransaction(); 90 | $retorno = $this->Repository->update($data, $id); 91 | $this->db->commit(); 92 | return $retorno; 93 | }catch(\Exception $e){ 94 | $this->db->rollback(); 95 | throw $e; 96 | } 97 | } 98 | 99 | public function destroy($id) 100 | { 101 | try{ 102 | $this->db->beginTransaction(); 103 | $this->Repository->delete($id); 104 | $this->db->commit(); 105 | }catch(\Exception $e){ 106 | $this->db->rollback(); 107 | throw $e; 108 | } 109 | } 110 | 111 | public function getRepository() 112 | { 113 | return $this->Repository; 114 | } 115 | 116 | public function __call($method, $arguments = []) 117 | { 118 | return call_user_func_array([$this->Repository, $method], $arguments); 119 | } 120 | 121 | 122 | } -------------------------------------------------------------------------------- /src/Console/Commands/RepositoryCommand.php: -------------------------------------------------------------------------------- 1 | createRepository(); 53 | $this->createService(); 54 | }catch(\Exception $e){ 55 | return $this->error($e->getMessage()); 56 | } 57 | } 58 | 59 | 60 | public function createRepository() 61 | { 62 | $name = 'Repositories/'.$this->argument('name'); 63 | $n = explode('/', $this->argument('name')); 64 | $n = array_pop($n); 65 | $this->name = $n.'Repository'; 66 | 67 | $this->entity = $this->option('entity'); 68 | 69 | 70 | 71 | if(!file_exists($path = $this->getPath($name.'Repository'))) 72 | { 73 | 74 | $this->files->makeDirectory(dirname($path), 0777, true, true); 75 | 76 | } 77 | 78 | if(file_exists($name)) 79 | { 80 | return $this->error('There is a file with the same name'); 81 | } 82 | 83 | $fp = fopen($path, 'w+'); 84 | if(!fputs($fp,$this->renderRepositoryClass())) throw new \Exception('It can not create the repository file with the given name. Check if you have permission to create it.'); 85 | return $this->info('Repository file successfully created'); 86 | 87 | } 88 | 89 | 90 | public function createService() 91 | { 92 | $nname = str_replace('Repository', 'Service', $this->argument('name')); 93 | $name = 'Services/'.$nname; 94 | $n = explode('/', $nname); 95 | $n = array_pop($n); 96 | $this->name = $n.'Service'; 97 | $this->entity = $this->option('entity'); 98 | 99 | 100 | if(!file_exists($path = $this->getPath($name.'Service'))) 101 | { 102 | 103 | $this->files->makeDirectory(dirname($path), 0777, true, true); 104 | 105 | } 106 | 107 | if(file_exists($name)) 108 | { 109 | return $this->error('There is a file with the same name'); 110 | } 111 | 112 | $fp = fopen($path, 'w+'); 113 | if(!fputs($fp,$this->renderServiceClass())) throw new \Exception('It can not create the service file with the given name. Check if you have permission to create it.'); 114 | return $this->info('Service file successfully created!'); 115 | 116 | } 117 | 118 | 119 | protected function renderRepositoryClass() 120 | { 121 | $str = $this->files->get(__DIR__.'/Stubs/RepositoryStub.stub'); 122 | $namespace = $this->getAppNamespace().'Repositories/'.$this->argument('name'); 123 | $c = explode('/', $namespace); 124 | $classe = array_pop($c); 125 | $namespace = str_replace('/', '\\', $namespace); 126 | $namespace = explode('\\', $namespace); 127 | array_pop($namespace); 128 | $namespace = implode('\\', $namespace); 129 | $str = str_replace('DummyNamespace', $namespace, $str); 130 | $str = str_replace('DummyClass', $classe.'Repository', $str); 131 | 132 | $entity = str_replace('/', '\\', $this->option('entity')); 133 | $str = str_replace('DummyEntity', '\\'.$entity, $str); 134 | $small = explode('\\', $entity); 135 | $small = array_pop($small); 136 | $str = str_replace('DummySmallEntity', $small, $str); 137 | //$str = str_replace('$entity', '$'.strtolower($small), $str); 138 | //$str = str_replace('$this->entity', '$this->'.strtolower($small), $str); 139 | return $str; 140 | } 141 | 142 | 143 | 144 | protected function renderServiceClass() 145 | { 146 | $str = $this->files->get(__DIR__.'/Stubs/ServicesStub.stub'); 147 | $namespace = $this->getAppNamespace().'Services/'.$this->argument('name').'Service'; 148 | $c = explode('/', $namespace); 149 | $classe = array_pop($c); 150 | $namespace = str_replace('/', '\\', $namespace); 151 | $namespace = explode('\\', $namespace); 152 | array_pop($namespace); 153 | $namespace = implode('\\', $namespace); 154 | 155 | $str = str_replace('DummyNamespace', $namespace, $str); 156 | $str = str_replace('DummyClass', $classe, $str); 157 | $str = str_replace('DummyRepository', '\\'.$this->getAppNamespace().'Repositories\\'.str_replace('/', '\\', $this->argument('name').'Repository'), $str); 158 | 159 | 160 | $small = explode('/', $this->argument('name')); 161 | $small = array_pop($small).'Repository'; 162 | $str = str_replace('DummySmallRepository', $small, $str); 163 | 164 | $entity = str_replace('/', '\\', $this->option('entity')); 165 | $str = str_replace('DummyEntity', '\\'.$this->getAppNamespace().$entity, $str); 166 | $small = explode('\\', $entity); 167 | $small = array_pop($small); 168 | $str = str_replace('DummySmallEntity', $small, $str); 169 | 170 | return $str; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/Console/Commands/Stubs/RepositoryStub.stub: -------------------------------------------------------------------------------- 1 | query = $query; 29 | $this->type = $type; 30 | $this->fields = $fields; 31 | } 32 | 33 | public function apply(Model $model, RepositoryContract $repository) 34 | { 35 | $fields = $this->fields; 36 | $query = $this->query; 37 | if(!$fields) 38 | $fields = $model->getFillable(); 39 | 40 | switch($this->type){ 41 | case self::$ST_USE_AT_BEGIN: $query = '%'.$this->query; break; 42 | case self::$ST_USE_AT_END: $query = $this->query.'%'; break; 43 | case self::$ST_USE_AT_BOTH: $query = '%'.$this->query.'%'; break; 44 | } 45 | $query = $model->where(function($q) use($fields, $query){ 46 | foreach($fields as $i => $field) 47 | { 48 | if($i == 0){ 49 | $q->where($field, 'like', $query); 50 | continue; 51 | } 52 | $q->orWhere($field, 'like', $query); 53 | } 54 | }); 55 | 56 | return $query; 57 | } 58 | 59 | 60 | 61 | } -------------------------------------------------------------------------------- /src/Exceptions/RepositoryException.php: -------------------------------------------------------------------------------- 1 | commands($this->commands); 31 | } 32 | } 33 | --------------------------------------------------------------------------------