Skip to content

Commit 127fdeb

Browse files
Merge branch 'master' into feature/pivot-table
2 parents 8e9a022 + c64781c commit 127fdeb

File tree

8 files changed

+380
-1
lines changed

8 files changed

+380
-1
lines changed

README.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,178 @@ The command will create a new migration in ```database/migrations```. Run the mi
188188
php artisan migrate
189189
```
190190

191+
# Service Generator
192+
193+
## Table of Contents
194+
<ol>
195+
<li><a href="#features">Features</a></li>
196+
<li><a href="#installation">Installation</a></li>
197+
<li>
198+
<a href="#usage">Usage</a>
199+
<ul>
200+
<li><a href="#generate-services">Generate services</a></li>
201+
<li><a href="#generate-services-for-models">Generate services for models</a></li>
202+
<li><a href="#generate-services-for-controllers">Generate services for controllers</a></li>
203+
</ul>
204+
</li>
205+
<li>
206+
<a href="#the-service-pattern">The service pattern</a>
207+
<ul>
208+
<li><a href="#when-to-use-the-service-pattern">When to use the service pattern</a></li>
209+
<li>
210+
<a href="#how-to-use-services">How to use services</a>
211+
<ul>
212+
<li><a href="#static-methods">Static methods</a></li>
213+
<li><a href="#depency-injection">Dependency Injection</a></li>
214+
</ul>
215+
</li>
216+
</ul>
217+
</li>
218+
<li><a href="#more-generator-packages">More generator packages</a></li>
219+
<li><a href="#contributing">Contributing</a></li>
220+
<li><a href="#license">License</a></li>
221+
</ol>
222+
223+
## Usage
224+
After installation the ```php artisan make:service {name}``` will be available in the list
225+
of artisan commands.
226+
227+
### Generate Service
228+
To generate a new service use the following artisan command.
229+
```bash
230+
php artisan make:service UserService
231+
```
232+
233+
### Generate a service for a model
234+
Add a ```--service``` or ```-S``` param to generate a service for the model.
235+
```bash
236+
php artisan make:model Post --service
237+
```
238+
239+
Use the ```-a``` or ```--all``` param to generate a service, migration, seeder, factory, policy,
240+
and resource controller for the model.
241+
```bash
242+
php artisan make:model Post --all
243+
```
244+
245+
### Generate a service for a controller
246+
Add a ```--service``` or ```-S``` param to generate a service for the controller.
247+
248+
```bash
249+
php artisan make:controller PostController --service
250+
```
251+
252+
## The service pattern
253+
254+
### When to use the service pattern
255+
A common question is: where do I put my business logic? You want to keep your models thin and your controller functions
256+
skinny. There are multiple ways to archive this, extracting your business logic to the
257+
service layer is a common method. By encapsulating your business logic in a service class you
258+
are able to re-use the logic for example in your controllers, commands, jobs and middelware.
259+
260+
### How to use services
261+
Once you have made a service it is time to add your business logic. We will discus how to use a service via static methods,
262+
dependency injection and how to use it with interfaces and repositories.
263+
264+
#### Static methods
265+
a common way to use a service is to call it's methods statically. It is similar to helper functions. Let's say we have
266+
a ```PostService``` with a method to get a post based on a slug.
267+
268+
```php
269+
namespace App\Services;
270+
271+
use App\Models\Post;
272+
273+
class PostService
274+
{
275+
// Declare the function as static
276+
public static function getPostBySlug(string $slug): Post
277+
{
278+
return Post::with('tags')
279+
->where('slug', $slug)
280+
->get();
281+
}
282+
}
283+
```
284+
285+
Next you can include the service class for example your controller and call the ```getPostBySlug``` method statically.
286+
```php
287+
namespace App\Http\Controllers;
288+
289+
// Include the service
290+
use App\Services\PostService;
291+
292+
class PostController extends Controller
293+
{
294+
public function show(string $slug)
295+
{
296+
// Call the method statically from the service class
297+
$post = PostService::getPostBySlug($slug);
298+
299+
return view('posts.show', compact('post'));
300+
}
301+
}#
302+
```
303+
304+
The ```getPostBySlug``` method is in this example a very simple function but as you can see it keeps you controller skinny
305+
and and your business logic seperated. Keep in mind that static classes and methods are stateless. The class won't save
306+
any data in itself.
307+
308+
#### Dependency Injection
309+
Another popular method is to use services with dependency injection. With dependency injection you can write loosely
310+
coupled code. When done right this will improve the flexibility and maintainability of your code.
311+
312+
The ```PostService``` we used as example before will remain
313+
almost the same except we don't declare the functions inside the class as static anymore.
314+
315+
```php
316+
namespace App\Services;
317+
318+
use App\Models\Post;
319+
320+
class PostService
321+
{
322+
public function getPostBySlug(string $slug): Post
323+
{
324+
return Post::with('tags')
325+
->where('slug', $slug)
326+
->get();
327+
}
328+
}
329+
```
330+
331+
Next we inject the service into the constructor of the class where we want to use it. Inside the constructor we
332+
assign the object to the ```$postService``` class property. Now the ```$postService``` property will be callable in
333+
all functions within the class with ```$this->postService```. While typing your IDE will already typehint the functions
334+
in your PostService class, in this case only ```->getPostBySlug($slug)```.
335+
```php
336+
namespace App\Http\Controllers;
337+
338+
// Include the service
339+
use App\Services\PostService;
340+
341+
class PostController extends Controller
342+
{
343+
// Declare the property
344+
protected $postService;
345+
346+
// Inject the service into the constructor
347+
public function __construct(PostService $postService)
348+
{
349+
// Assign the service instance to the class property
350+
$this->postService = $postService;
351+
}
352+
353+
public function show($slug)
354+
{
355+
// Call the method you need from the service via the class property
356+
$post = $this->postService->getPostBySlug($slug);
357+
358+
return view('posts.show', compact('post'));
359+
}
360+
}
361+
```
362+
191363
## Contributing
192364

193365
- [Bhargav Raviya](https://github.com/bhargavraviya)

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"artisan",
1616
"pivot",
1717
"migrations"
18+
"services",
19+
"pattern"
1820
],
1921
"license": "MIT",
2022
"authors": [
@@ -33,7 +35,8 @@
3335
"extra": {
3436
"laravel": {
3537
"providers": [
36-
"RajTechnologies\\Tools\\ToolServiceProvider"
38+
"RajTechnologies\\Tools\\ToolServiceProvider",
39+
"RajTechnologies\\Tools\\Http\\Providers\\CommandServiceProvider"
3740
]
3841
}
3942
}

src/Console/MakeController.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace RajTechnologies\Tools\Console;
4+
5+
use Illuminate\Routing\Console\ControllerMakeCommand;
6+
use Illuminate\Support\Str;
7+
use Symfony\Component\Console\Input\InputOption;
8+
9+
class MakeController extends ControllerMakeCommand
10+
{
11+
/**
12+
* @return void
13+
*/
14+
public function handle()
15+
{
16+
parent::handle();
17+
18+
if ($this->option('service')) {
19+
$this->createService();
20+
}
21+
}
22+
23+
/**
24+
* Create a model factory for the model.
25+
*
26+
* @return void
27+
*/
28+
protected function createService()
29+
{
30+
$nameInput = Str::replace('Controller', '', $this->getNameInput());
31+
$name = Str::studly($nameInput);
32+
33+
$this->call('make:service', [
34+
'name' => "{$name}Service"
35+
]);
36+
}
37+
38+
/**
39+
* @return array
40+
*/
41+
public function getOptions(): array
42+
{
43+
$options = parent::getOptions();
44+
$options[] = ['service', 's', InputOption::VALUE_NONE, 'Generate a service for the controller'];
45+
46+
return $options;
47+
}
48+
}

src/Console/MakeModel.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace RajTechnologies\Tools\Console;
4+
5+
use Illuminate\Foundation\Console\ModelMakeCommand;
6+
use Illuminate\Support\Str;
7+
use Symfony\Component\Console\Input\InputOption;
8+
9+
class MakeModel extends ModelMakeCommand
10+
{
11+
/**
12+
* @return void
13+
*/
14+
public function handle()
15+
{
16+
parent::handle();
17+
18+
if ($this->option('all')) {
19+
$this->input->setOption('service', true);
20+
}
21+
22+
if ($this->option('service')) {
23+
$this->createService();
24+
}
25+
}
26+
27+
/**
28+
* Create a model factory for the model.
29+
*
30+
* @return void
31+
*/
32+
protected function createService()
33+
{
34+
$name = Str::studly($this->getNameInput());
35+
36+
$this->call('make:service', [
37+
'name' => "{$name}Service"
38+
]);
39+
}
40+
41+
/**
42+
* @return array
43+
*/
44+
public function getOptions(): array
45+
{
46+
$options = parent::getOptions();
47+
$options[] = ['service', 'S', InputOption::VALUE_NONE, 'Generate a service for the model'];
48+
49+
return $options;
50+
}
51+
}

src/Console/MakeService.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace RajTechnologies\Tools\Console;
4+
5+
use Illuminate\Console\GeneratorCommand;
6+
7+
class MakeService extends GeneratorCommand
8+
{
9+
/**
10+
* The name and signature of the console command.
11+
*
12+
* @var string
13+
*/
14+
protected $signature = 'make:service {name}';
15+
16+
/**
17+
* The console command description.
18+
*
19+
* @var string
20+
*/
21+
protected $description = 'Create a new service class';
22+
23+
/**
24+
* The type of class being generated.
25+
*
26+
* @var string
27+
*/
28+
protected $type = 'Service';
29+
30+
/**
31+
* Get the stub file for the generator.
32+
*
33+
* @return string
34+
*/
35+
protected function getStub()
36+
{
37+
return __DIR__.'/../../stubs/service.stub';
38+
}
39+
40+
/**
41+
* Get the default namespace for the class.
42+
*
43+
* @param string $rootNamespace
44+
* @return string
45+
*/
46+
protected function getDefaultNamespace($rootNamespace)
47+
{
48+
return $rootNamespace.'\Services';
49+
}
50+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace RajTechnologies\Tools\Http\Providers;
4+
5+
use Illuminate\Foundation\Providers\ArtisanServiceProvider;
6+
use RajTechnologies\Tools\Console\MakeController;
7+
use RajTechnologies\Tools\Console\MakeModel;
8+
9+
class CommandServiceProvider extends ArtisanServiceProvider
10+
{
11+
/**
12+
* Register the command.
13+
*
14+
* @return void
15+
*/
16+
protected function registerModelMakeCommand()
17+
{
18+
$this->app->singleton('command.model.make', function ($app) {
19+
return new MakeModel($app['files']);
20+
});
21+
}
22+
23+
/**
24+
* Register the command.
25+
*
26+
* @return void
27+
*/
28+
protected function registerControllerMakeCommand()
29+
{
30+
$this->app->singleton('command.controller.make', function ($app) {
31+
return new MakeController($app['files']);
32+
});
33+
}
34+
}

0 commit comments

Comments
 (0)