Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit c989ab1

Browse files
author
fd6130
committed
add maker command and update readme
1 parent 2a1cb89 commit c989ab1

10 files changed

+266
-40
lines changed

README.md

+40-39
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,74 @@
1-
League Fractal Symfony Bundle
1+
Fractal Bundle
22
=============================
33

44
This bundle provides integration of [league/fractal](https://github.com/thephpleague/fractal) for Symfony. In addition it allows you to use [transformers as a services](#using-transformers-as-services).
55

6+
**This is a fork version of [samjarrett/FractalBundle](https://github.com/samjarrett/FractalBundle).**
7+
68
## Getting Started
79

8-
First of all you need to add dependency to composer.json:
10+
Requirements:
11+
12+
* PHP >= 7.4
13+
* Symfony 4, 5 and 6
14+
15+
Install through composer:
916

1017
```
11-
composer require samj/fractal-bundle
18+
composer require fd6130/fractal-bundle
1219
```
1320

14-
Then register bundle in `app/AppKernel.php`:
21+
If you are using symfony flex, it will automatic register the bundle for you.
22+
23+
## Usage
24+
25+
You can use command `php bin/console make:fractal-transformer` to create a transformer.
26+
27+
Or, just create it by your own and place it in `src/Transformer`.
1528

1629
```php
17-
public function registerBundles()
18-
{
19-
return [
20-
// ...
21-
new SamJ\FractalBundle\SamJFractalBundle(),
22-
];
30+
class UserTransformer extends TransformerAbstract
31+
{
32+
public function transform(User $user): array
33+
{
34+
$data = [
35+
'id' => $user->id(),
36+
'name' => $user->name(),
37+
];
38+
39+
return $data;
40+
}
2341
}
24-
```
2542

26-
Now we can write and use fractal transformers:
43+
$resource = new Collection($users, UserTransformer::class);
44+
45+
$response = $manager->createData($resource)->toArray();
46+
```
2747

28-
## Using Transformers as Services
48+
### Inject services to the transformers
2949

30-
There are several cases when you need to pass some dependencies into transformer. The common one is [role/scope based results](https://github.com/thephpleague/fractal/issues/327) in transformers. For example you need to show `email` field only for administrators or owner of user profile:
50+
You can inject services to your transformer through constructor:
3151

3252
```php
3353
class UserTransformer extends TransformerAbstract
3454
{
35-
private $authorizationCheker;
55+
private $entityManager;
3656

37-
public function __construct(AuthorizationChecker $authorizationCheker)
57+
public function __construct(EntityManagerInterface $entityManager)
3858
{
39-
$this->authorizationCheker = $authorizationCheker;
59+
$this->entityManager = $entityManager;
4060
}
4161

42-
public function transform(User $user)
62+
public function transform(User $user): array
4363
{
4464
$data = [
4565
'id' => $user->id(),
4666
'name' => $user->name(),
4767
];
48-
49-
if ($this->authorizationChecker->isGranted(UserVoter::SEE_EMAIL, $user)) {
50-
$data['email'] = $user->email();
51-
}
68+
69+
// $this->entityManager->getRepository(...)
5270

5371
return $data;
5472
}
5573
}
5674
```
57-
58-
Then you could just register this class as service, and pass service ID as transformer. This bundle then will try to get it from container:
59-
60-
```php
61-
$resource = new Collection($users, 'app.transformer.user');
62-
```
63-
64-
This works in includes as well:
65-
66-
```php
67-
public function includeFriends(User $user)
68-
{
69-
return $this->collection($user->friends(), 'app.transformer.user');
70-
}
71-
```
72-
73-
You could see example of how to use transformers in [sample application](tests/Fixtures) which is used in test suites.

composer.json

+3
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@
1717
"php": ">=7.4",
1818
"league/fractal": "~0.17",
1919
"symfony/framework-bundle": "^4.0|^5.0|^6.0"
20+
},
21+
"require-dev": {
22+
"symfony/maker-bundle": "^1.0"
2023
}
2124
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Fd\FractalBundle\DependencyInjection\Compiler;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\Definition;
8+
9+
class TransformerPass implements CompilerPassInterface
10+
{
11+
const TRANSFORMER_TAG = 'fd_fractal.transformer';
12+
public function process(ContainerBuilder $container)
13+
{
14+
$taggedServices = $container->findTaggedServiceIds(self::TRANSFORMER_TAG);
15+
16+
foreach ($taggedServices as $id => $tags) {
17+
$definition = $container->findDefinition($id);
18+
$definition->setPublic(true);
19+
}
20+
}
21+
}

src/DependencyInjection/FdFractalExtension.php

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Fd\FractalBundle\DependencyInjection;
44

5+
use Fd\FractalBundle\DependencyInjection\Compiler\TransformerPass;
6+
use League\Fractal\TransformerAbstract;
57
use Symfony\Component\DependencyInjection\ContainerBuilder;
68
use Symfony\Component\Config\FileLocator;
79
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
@@ -16,5 +18,9 @@ public function load(array $configs, ContainerBuilder $container)
1618
{
1719
$loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
1820
$loader->load('services.php');
21+
22+
$container->registerForAutoconfiguration(TransformerAbstract::class)
23+
->addTag(TransformerPass::TRANSFORMER_TAG)
24+
;
1925
}
2026
}

src/FdFractalBundle.php

+8
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22

33
namespace Fd\FractalBundle;
44

5+
use Fd\FractalBundle\DependencyInjection\Compiler\TransformerPass;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
57
use Symfony\Component\HttpKernel\Bundle\Bundle;
68

79
class FdFractalBundle extends Bundle
810
{
11+
public function build(ContainerBuilder $container)
12+
{
13+
parent::build($container);
14+
15+
$container->addCompilerPass(new TransformerPass);
16+
}
917
}

src/Maker/MakeFractalTransformer.php

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
namespace Fd\FractalBundle\Maker;
4+
5+
use League\Fractal\TransformerAbstract;
6+
use Symfony\Bundle\MakerBundle\ConsoleStyle;
7+
use Symfony\Bundle\MakerBundle\DependencyBuilder;
8+
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
9+
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
10+
use Symfony\Bundle\MakerBundle\Generator;
11+
use Symfony\Bundle\MakerBundle\InputConfiguration;
12+
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
13+
use Symfony\Bundle\MakerBundle\Str;
14+
use Symfony\Bundle\MakerBundle\Validator;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Input\InputArgument;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Input\InputOption;
19+
use Symfony\Component\Console\Question\Question;
20+
21+
final class MakeFractalTransformer extends AbstractMaker
22+
{
23+
use MakerTrait;
24+
25+
private $doctrineHelper;
26+
27+
public function __construct(DoctrineHelper $doctrineHelper)
28+
{
29+
$this->doctrineHelper = $doctrineHelper;
30+
}
31+
32+
public static function getCommandName(): string
33+
{
34+
return 'make:fractal-transformer';
35+
}
36+
37+
public static function getCommandDescription(): string
38+
{
39+
return 'Create a transformer class for transforming data.';
40+
}
41+
42+
public function configureCommand(Command $command, InputConfiguration $inputConf)
43+
{
44+
$command
45+
->setDescription('Creates a new transformer class')
46+
->addArgument('entity-class', InputArgument::OPTIONAL, sprintf('The class name of the entity to create Transformer (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())))
47+
->addArgument('class-name', InputArgument::OPTIONAL, sprintf('The class name of new Transformer (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())))
48+
->addOption('no-entity', null, InputOption::VALUE_NONE, 'Use this option to generate a plank transformer')
49+
->setHelp(file_get_contents(__DIR__ . '/../Resources/help/MakeFractalTransformer.txt'));
50+
51+
$inputConf->setArgumentAsNonInteractive('entity-class');
52+
$inputConf->setArgumentAsNonInteractive('class-name');
53+
}
54+
55+
public function interact(InputInterface $input, ConsoleStyle $io, Command $command)
56+
{
57+
if ($input->getOption('no-entity')) {
58+
$io->block([
59+
'Note: You have choose to generate a blank Transformer.',
60+
], null, 'fg=yellow');
61+
$classname = $io->ask(sprintf('The class name of new Transformer (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())), null, [Validator::class, 'notBlank']);
62+
63+
$input->setArgument('class-name', $classname);
64+
}
65+
else
66+
{
67+
$argument = $command->getDefinition()->getArgument('entity-class');
68+
$question = $this->createEntityClassQuestion($argument->getDescription());
69+
$value = $io->askQuestion($question);
70+
71+
$input->setArgument('entity-class', $value);
72+
}
73+
}
74+
75+
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
76+
{
77+
$noEntity = $input->getOption('no-entity');
78+
$classname = !$noEntity ? null : Str::asClassName($input->getArgument('class-name'));
79+
80+
$entityClassDetails = !$noEntity ? $generator->createClassNameDetails(
81+
Validator::entityExists($input->getArgument('entity-class'), $this->doctrineHelper->getEntitiesForAutocomplete()),
82+
'Entity\\'
83+
) : null;
84+
85+
$transformerClassDetails = !$noEntity ? $generator->createClassNameDetails(
86+
$entityClassDetails->getShortName(),
87+
'Transformer\\',
88+
'Transformer'
89+
) : $generator->createClassNameDetails($classname, 'Transformer\\', 'Transformer');
90+
91+
$generator->generateClass(
92+
$transformerClassDetails->getFullName(),
93+
__DIR__ . '/../Resources/skeleton/Transformer.tpl.php',
94+
[
95+
'no_entity' => $noEntity,
96+
'entity_class_name' => $entityClassDetails ? $entityClassDetails->getShortName() : null,
97+
'entity_variable_name' => $entityClassDetails ? Str::asLowerCamelCase($entityClassDetails->getShortName()) : null,
98+
'entity_full_class_name' => $entityClassDetails ? $entityClassDetails->getFullName(): null,
99+
]
100+
);
101+
102+
$generator->writeChanges();
103+
104+
$this->writeSuccessMessage($io);
105+
106+
$io->text([
107+
'Next: Open your new transformer class and start customizing it.',
108+
]);
109+
}
110+
111+
public function configureDependencies(DependencyBuilder $dependencies)
112+
{
113+
$dependencies->addClassDependency(TransformerAbstract::class, 'fd6130/fractal-bundle');
114+
}
115+
116+
private function createEntityClassQuestion(string $questionText): Question
117+
{
118+
$question = new Question($questionText);
119+
$question->setValidator([Validator::class, 'notBlank']);
120+
$question->setAutocompleterValues($this->doctrineHelper->getEntitiesForAutocomplete());
121+
122+
return $question;
123+
}
124+
}

src/Maker/MakerTrait.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Fd\FractalBundle\Maker;
4+
5+
use Symfony\Bundle\MakerBundle\Validator;
6+
use Symfony\Component\Console\Question\Question;
7+
8+
trait MakerTrait
9+
{
10+
private function createEntityClassQuestion(string $questionText): Question
11+
{
12+
$question = new Question($questionText);
13+
$question->setValidator([Validator::class, 'notBlank']);
14+
$question->setAutocompleterValues($this->doctrineHelper->getEntitiesForAutocomplete());
15+
16+
return $question;
17+
}
18+
}

src/Resources/config/services.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
<?php
22

33
use Fd\FractalBundle\ContainerAwareManager;
4+
use Fd\FractalBundle\Maker\MakeFractalTransformer;
45
use League\Fractal\Manager;
56
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
67

78
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
89

910
return static function (ContainerConfigurator $container) {
10-
$container->services()
11+
$services = $container->services();
12+
13+
$services
1114
->set(ContainerAwareManager::class, ContainerAwareManager::class)
1215
->public()
1316
->call('setContainer', [service('service_container')])
1417
->alias('fd_fractal.manager', ContainerAwareManager::class)
1518
->public()
1619
->alias(Manager::class, ContainerAwareManager::class)
1720
->public()
21+
22+
;
23+
$services->set("maker.maker.make_fractal_transformer", MakeFractalTransformer::class)
24+
->args([service("maker.doctrine_helper")])
25+
->tag("maker.command")
1826
;
1927
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
The <info>%command.name%</info> command generates a new transformer class.
2+
3+
<info>php %command.full_name%</info>
4+
5+
<info>php %command.full_name% --no-entity</info>
6+
7+
Arguments are optional.
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?= "<?php\n" ?>
2+
3+
namespace <?= $namespace; ?>;
4+
5+
use League\Fractal\TransformerAbstract;
6+
<?php if(!$no_entity): ?>use <?= $entity_full_class_name. ";\n"?><?php endif ?>
7+
use League\Fractal\Resource\Collection;
8+
use League\Fractal\Resource\Item;
9+
10+
class <?= $class_name ?> extends TransformerAbstract
11+
{
12+
/**
13+
* List of resources possible to include
14+
*
15+
* @var array
16+
*/
17+
protected $availableIncludes = [];
18+
19+
/**
20+
* List of resources to automatically include
21+
*
22+
* @var array
23+
*/
24+
protected $defaultIncludes = [];
25+
26+
public function transform(<?php if(!$no_entity):?><?= $entity_class_name ?> $entity<?php else:?>$entity<?php endif ?>): array
27+
{
28+
return [];
29+
}
30+
}

0 commit comments

Comments
 (0)