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

Commit b20d5f4

Browse files
authored
add import cookbook (#50)
* add import cookbook * add import cookbook
1 parent 8cd7c4c commit b20d5f4

File tree

4 files changed

+260
-35
lines changed

4 files changed

+260
-35
lines changed

docs/backend/cookbook.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@
1111
* [New Condition](backend/cookbook/new_condition.md)
1212
* [New Channel](backend/cookbook/new_channel.md)
1313
* [New Mass Action](backend/cookbook/new_mass_action.md)
14+
* [New Import Source](backend/cookbook/new_source.md)
1415

1516

docs/backend/cookbook/new_channel.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# How to create a new channel
33

44
Channel
5-
* Entity Claas to management
6-
* external service connector(if required)
5+
* Entity Class to management
6+
* external service connector (if required)
77
* specific configuration of a specific target system
88

99
In your channel [module].

docs/backend/cookbook/new_source.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# How to create new import source
2+
3+
## Source class
4+
5+
Any custom source class must extend```AbstractSource```
6+
7+
This class is used to define a new type of Source to appear in the application and can be used to store the necessary configuration data
8+
9+
```php
10+
namespace YourNameSpace\Domain\Entity;
11+
12+
use Ergonode\SharedKernel\Domain\Aggregate\SourceId;
13+
use Ergonode\Importer\Domain\Entity\Source\AbstractSource;
14+
15+
class YourSource extends AbstractSource
16+
{
17+
public const TYPE = 'your-source-type';
18+
19+
public function __construct(SourceId $id, string $name)
20+
{
21+
parent::__construct($id, $name);
22+
}
23+
24+
public function getType(): string
25+
{
26+
return self::TYPE;
27+
}
28+
}
29+
```
30+
31+
## Source class configuration
32+
33+
You need create Form class for configuration Your Source.
34+
35+
Only "name" field is required, other are dependent from you needs.
36+
37+
```php
38+
namespace YourNameSpace\Application\Form;
39+
40+
use YourNameSpace\Application\Model\YourSourceConfigurationModel;
41+
use Symfony\Component\Form\AbstractType;
42+
use Symfony\Component\Form\Extension\Core\Type\TextType;
43+
use Symfony\Component\Form\FormBuilderInterface;
44+
use Symfony\Component\OptionsResolver\OptionsResolver;
45+
46+
class YourSourceConfigurationForm extends AbstractType
47+
{
48+
/**
49+
* @param array $options
50+
*/
51+
public function buildForm(FormBuilderInterface $builder, array $options): void
52+
{
53+
$builder
54+
->add(
55+
'name',
56+
TextType::class,
57+
[
58+
'label' => 'Name',
59+
]
60+
);
61+
}
62+
63+
public function configureOptions(OptionsResolver $resolver): void
64+
{
65+
$resolver->setDefaults([
66+
'translation_domain' => 'importer',
67+
'data_class' => YourSourceConfigurationModel::class,
68+
'allow_extra_fields' => true,
69+
'label' => 'Import settings',
70+
]);
71+
}
72+
73+
public function getBlockPrefix(): ?string
74+
{
75+
return null;
76+
}
77+
}
78+
```
79+
80+
Configuration Model class
81+
82+
```php
83+
namespace YourNameSpace\Application\Model;
84+
85+
use Symfony\Component\Validator\Constraints as Assert;
86+
use YourNameSpace\Domain\Entity\YourSource;
87+
88+
class YourSourceConfigurationModel
89+
{
90+
/**
91+
* @Assert\NotBlank()
92+
* @Assert\Length(min=2)
93+
*/
94+
public ?string $name = null;
95+
96+
public function __construct(YourSource $source = null)
97+
{
98+
if ($source) {
99+
$this->name = $source->getName();
100+
}
101+
}
102+
}
103+
```
104+
105+
For providing form need to add ```YourSourceFormFactory``` class.
106+
107+
```php
108+
namespace YourNameSpace\Application\Factory;
109+
110+
use Ergonode\Importer\Domain\Entity\Source\AbstractSource;
111+
use YourNameSpace\Domain\Entity\YourSource;
112+
use Symfony\Component\Form\FormFactoryInterface;
113+
use YourNamespace\Application\Form\YourSourceConfigurationForm;
114+
use Symfony\Component\Form\FormInterface;
115+
use YourNamespace\Application\Model\YourSourceConfigurationModel;
116+
use Ergonode\Importer\Application\Provider\SourceFormFactoryInterface;
117+
use Symfony\Component\HttpFoundation\Request;
118+
119+
class YourSourceFormFactory implements SourceFormFactoryInterface
120+
{
121+
private FormFactoryInterface $formFactory;
122+
123+
public function __construct(FormFactoryInterface $formFactory)
124+
{
125+
$this->formFactory = $formFactory;
126+
}
127+
128+
public function supported(string $type): bool
129+
{
130+
return YourSource::TYPE === $type;
131+
}
132+
133+
public function create(AbstractSource $source = null): FormInterface
134+
{
135+
$model = new YourSourceConfigurationModel($source);
136+
if (null === $source) {
137+
return $this->formFactory->create(YourSourceConfigurationForm::class, $model);
138+
}
139+
140+
return $this->formFactory->create(
141+
YourSourceConfigurationForm::class,
142+
$model,
143+
['method' => Request::METHOD_PUT]
144+
);
145+
}
146+
}
147+
```
148+
149+
Next step is to create Source Manipulation Commands and Handlers
150+
151+
```php
152+
namespace YourNameSpace\Domain\Command;
153+
154+
use Ergonode\Importer\Domain\Command\CreateSourceCommandInterface;
155+
use Ergonode\SharedKernel\Domain\Aggregate\SourceId;
156+
use JMS\Serializer\Annotation as JMS;
157+
158+
class CreateYourSourceCommand implements CreateSourceCommandInterface
159+
{
160+
/**
161+
* @JMS\Type("Ergonode\SharedKernel\Domain\Aggregate\SourceId")
162+
*/
163+
private SourceId $id;
164+
165+
/**
166+
* @JMS\Type("string")
167+
*/
168+
private string $name;
169+
170+
public function __construct(
171+
SourceId $id,
172+
string $name
173+
) {
174+
$this->id = $id;
175+
$this->name = $name;
176+
}
177+
178+
public function getId(): SourceId
179+
{
180+
return $this->id;
181+
}
182+
183+
public function getName(): string
184+
{
185+
return $this->name;
186+
}
187+
}
188+
189+
```
190+
191+
```php
192+
namespace YourNameSpace\Infrastructure\Handler;
193+
194+
use Ergonode\Importer\Domain\Repository\SourceRepositoryInterface;
195+
use YourNameSpace\Domain\Command\CreateYourSourceCommand;
196+
use YourNameSpace\Domain\Entity\YourSource;
197+
198+
class YourSourceSourceCommandHandler
199+
{
200+
private SourceRepositoryInterface $repository;
201+
202+
public function __construct(SourceRepositoryInterface $repository)
203+
{
204+
$this->repository = $repository;
205+
}
206+
207+
/**
208+
* @throws \Exception
209+
*/
210+
public function __invoke(CreateYourSourceCommand $command): void
211+
{
212+
$source = new YourSource(
213+
$command->getId(),
214+
$command->getName()
215+
);
216+
217+
$this->repository->save($source);
218+
}
219+
}
220+
221+
```
222+
223+
At last add you source import processor.
224+
225+
```php
226+
namespace YourNameSpace\Infrastructure\Processor;
227+
228+
use Ergonode\Importer\Domain\Entity\Import;
229+
use Ergonode\Importer\Infrastructure\Processor\SourceImportProcessorInterface;
230+
use YourNameSpace\Domain\Entity\YourSource;
231+
232+
class YourSourceImportProcess implements SourceImportProcessorInterface
233+
{
234+
public function supported(string $type): bool
235+
{
236+
return $type === YourSource::TYPE;
237+
}
238+
239+
public function start(Import $import): void
240+
{
241+
// here is body of your import process
242+
}
243+
}
244+
```

docs/backend/modules/importer.md

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,26 @@
55
This module is responsible for managing imports to ergonode from external systems or files.
66

77

8-
## Reader
8+
## Custom Import error
99

10-
**Reader** is a component responsible for reading files in different formats.
10+
If an error occurs during the import process, we may want to inform the user by presenting the relevant content in the import error list.
1111

12-
One Reader class is responsible for one file type.
13-
14-
To use it you need to create a class which implements **FileReaderInterface**.
12+
For that we need to create custom error. We're doing that by creating new exception class extended from ```Ergonode\Importer\Infrastructure\Exception\ImportException```
1513

1614
```php
17-
<?php
18-
namespace Ergonode\Component\Importer\Infrastructure\Reader;
15+
namespace YourNameSpace\Infrastructure\Exception;
1916

20-
interface FileReaderInterface extends \IteratorAggregate, \Countable
17+
class YourCustomException extends ImportException
2118
{
22-
public function open(string $file, array $configuration = [], array $formatters = []): void;
23-
public function read(): \Traversable;
24-
public function close(): void;
25-
}
26-
27-
```
28-
29-
Example of **Reader** you can find here:
30-
`Ergonode\Component\Importer\Infrastructure\Reader\CsvFileReader.php`
31-
32-
33-
## Formatter
19+
private const MESSAGE = 'Your error message with param {param}';
3420

35-
**Formatters** are objects responsbile for changing every line from input file.
36-
37-
One **Formatter** class is responsible for one text editing operation. For example it can be used for removing some unwanted characters or changing text encoding.
38-
39-
You you need to create a class which implements **FormatterInterface**.
40-
41-
```php
42-
<?php
43-
namespace Ergonode\Component\Importer\Infrastructure\Formatter;
44-
45-
interface FormatterInterface
46-
{
47-
public function format(string $string): string;
21+
public function __construct(string $param, \Throwable $previous = null)
22+
{
23+
parent::__construct(self::MESSAGE, ['{param}' => $param], $previous);
24+
}
4825
}
4926
```
5027

28+
In addition to the importer.yml translation file, we can add a location-based version of our message
29+
30+
Now if you throw this exception in import process, message will be automatically added to import error list

0 commit comments

Comments
 (0)