|
| 1 | +Customizing Fixtures |
| 2 | +==================== |
| 3 | + |
| 4 | +What are fixtures? |
| 5 | +~~~~~~~~~~~~~~~~~~ |
| 6 | + |
| 7 | +Fixtures are just plain old PHP objects, that change system state during their execution - they can either |
| 8 | +persist some entities in the database, upload some files, dispatch some events or do anything you think is needed. |
| 9 | + |
| 10 | +.. code-block:: yaml |
| 11 | +
|
| 12 | + sylius_fixtures: |
| 13 | + suites: |
| 14 | + my_suite_name: |
| 15 | + fixtures: |
| 16 | + my_fixture: # Fixture name as a key |
| 17 | + priority: 0 # The higher priority is, the sooner the fixture will be executed |
| 18 | + options: ~ # Fixture options |
| 19 | +
|
| 20 | +They implement the ``Sylius\Bundle\FixturesBundle\Fixture\FixtureInterface`` and need to be registered under |
| 21 | +the ``sylius_fixtures.fixture`` tag in order to be used in suite configuration. |
| 22 | + |
| 23 | +.. note:: |
| 24 | + |
| 25 | + The former interface extends the ``ConfigurationInterface``, which is widely known from ``Configuration`` classes |
| 26 | + placed under ``DependencyInjection`` directory in Symfony bundles. |
| 27 | + |
| 28 | +Why would you customize a fixtures? |
| 29 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 30 | + |
| 31 | +If you want to operate on the test data of objects appearing in your store |
| 32 | +e.g: ``Currency``, ``Country``, ``User`` etc. |
| 33 | + |
| 34 | +How to add new Fixtures? |
| 35 | +~~~~~~~~~~~~~~~~~~~~~~~~ |
| 36 | + |
| 37 | +In Sylius part of fixtures are added in ``src/Sylius/Bundle/CoreBundle/Resources/config/app/fixtures.yml``, where is defined ``default`` suite that is partially-configured. |
| 38 | +If you are planning to add new fixtures in default command ``sylius:fixtures:load``, only what you have to do is add new items in ``config\packages\_sylius.yml`` |
| 39 | + |
| 40 | +Adding new items in shop by standard yaml configuration: |
| 41 | + |
| 42 | +.. code-block:: yaml |
| 43 | +
|
| 44 | + sylius_fixtures: |
| 45 | + suites: |
| 46 | + default: # this key is always called wherever we use sylius:fixtures:load, below we are extending that about new fixtures |
| 47 | + fixtures: |
| 48 | + currency: |
| 49 | + options: |
| 50 | + currencies: ['PLN','HUF','EUR'] |
| 51 | + channel: |
| 52 | + options: |
| 53 | + custom: |
| 54 | + pl_web_store: # creating new channel |
| 55 | + name: "PL Web Store" |
| 56 | + code: "PL_WEB" |
| 57 | + locales: |
| 58 | + - "%locale%" |
| 59 | + currencies: |
| 60 | + - "PLN" |
| 61 | + enabled: true |
| 62 | + hostname: "localhost" |
| 63 | + hun_web_store: |
| 64 | + name: "Hun Web Store" |
| 65 | + code: "HUN_WEB" |
| 66 | + locales: |
| 67 | + - "%locale%" |
| 68 | + currencies: |
| 69 | + - "HUF" |
| 70 | + enabled: true |
| 71 | + hostname: "localhost" |
| 72 | + shipping_method: |
| 73 | + options: |
| 74 | + custom: |
| 75 | + ups_eu: # creating new shipping_method and adding channel to it |
| 76 | + code: "ups_eu" |
| 77 | + name: "UPS_eu" |
| 78 | + enabled: true |
| 79 | + channels: |
| 80 | + - "PL_WEB" |
| 81 | + ups: # adding channel to existing shipping_method |
| 82 | + channels: |
| 83 | + - "HUN_WEB" |
| 84 | + payment_method: |
| 85 | + options: |
| 86 | + custom: |
| 87 | + cash_on_delivery_pl: |
| 88 | + code: "cash_on_delivery_eu" |
| 89 | + name: "Cash on delivery_eu" |
| 90 | + channels: |
| 91 | + - "PL_WEB" |
| 92 | + bank_transfer: |
| 93 | + code: "bank_transfer_eu" |
| 94 | + name: "Bank transfer_eu" |
| 95 | + channels: |
| 96 | + - "PL_WEB" |
| 97 | + - "HUN_WEB" |
| 98 | + enabled: true |
| 99 | +
|
| 100 | +Fixtures in sylius are loaded and initialized using class located at ``src/Sylius/Bundle/CoreBundle/Fixture/*`` |
| 101 | + |
| 102 | +Below is presented class ``Sylius\Bundle\CoreBundle\Fixture\CurrencyFixture`` that load our currencies from yaml configuration: |
| 103 | + |
| 104 | +.. code-block:: php |
| 105 | +
|
| 106 | + <?php |
| 107 | +
|
| 108 | + ... |
| 109 | +
|
| 110 | + class CurrencyFixture extends AbstractFixture |
| 111 | + { |
| 112 | + ... |
| 113 | +
|
| 114 | + // here we load our options array from yaml file |
| 115 | + public function load(array $options): void |
| 116 | + { |
| 117 | + foreach ($options['currencies'] as $currencyCode) { |
| 118 | + /** @var CurrencyInterface $currency */ |
| 119 | + $currency = $this->currencyFactory->createNew(); |
| 120 | +
|
| 121 | + $currency->setCode($currencyCode); |
| 122 | +
|
| 123 | + $this->currencyManager->persist($currency); |
| 124 | + } |
| 125 | +
|
| 126 | + $this->currencyManager->flush(); |
| 127 | + } |
| 128 | +
|
| 129 | + ... |
| 130 | +
|
| 131 | + // here we configure our restriction from our input |
| 132 | + protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void |
| 133 | + { |
| 134 | + $optionsNode |
| 135 | + ->children() |
| 136 | + ->arrayNode('currencies') |
| 137 | + ->scalarPrototype() |
| 138 | + ; |
| 139 | + } |
| 140 | + } |
| 141 | +
|
| 142 | +This fixture is registered in ``src/Sylius/Bundle/CoreBundle/Resources/config/services/fixtures.xml``: |
| 143 | + |
| 144 | +.. code-block:: xml |
| 145 | +
|
| 146 | + <service id="sylius.fixture.currency" class="Sylius\Bundle\CoreBundle\Fixture\CurrencyFixture"> |
| 147 | + <argument type="service" id="sylius.factory.currency" /> |
| 148 | + <argument type="service" id="sylius.manager.currency" /> |
| 149 | + <tag name="sylius_fixtures.fixture" /> |
| 150 | + </service> |
| 151 | +
|
| 152 | +``Currency`` is simple and short example created fixtures but many models need additionally factory to create new items. |
| 153 | +Factories must implements ``Sylius\Bundle\CoreBundle\Fixture\Factory\ExampleFactoryInterface`` and ``Sylius\Bundle\CoreBundle\Fixture\Factory\AbstractExampleFactory`` |
| 154 | + |
| 155 | +`Products` are most complicated because have more dependencies than rest models |
| 156 | +Sylius delivered four ready implementation of ``Product``: |
| 157 | + |
| 158 | +* BookProductFixture |
| 159 | +* MugProductFixture |
| 160 | +* StickerProductFixture |
| 161 | +* TshirtProductFixture |
| 162 | + |
| 163 | +How to add new custom Fixtures? |
| 164 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 165 | + |
| 166 | +If you want to write custom fixture you must create class that implements ``Sylius/Bundle/FixturesBundle/Fixture/FixtureInterface`` |
| 167 | + |
| 168 | +Best ways to write custom fixture is extend class ``Sylius/Bundle/FixturesBundle/Fixture/AbstractFixture`` or ``Sylius/Bundles/FixturesBundle/Fixture/AbstractResourceFixture``: |
| 169 | + |
| 170 | +* Extending ``AbstractFixtures`` that is basic class giving only sample configuration, next we must override methods: load() and configureOptionsNode() |
| 171 | +* In most cases enought extend ``AbstractResourceFixture``, this class is partially-configured, only what we have to do is override configureOptionsNode() |
| 172 | + |
| 173 | +How to add new custom Fixtures in custom Models? |
| 174 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 175 | + |
| 176 | +.. tip:: |
| 177 | + |
| 178 | + Below Example is based on other example showing how to extends entity with a new field. |
| 179 | + You can browse the full implementation of this example on `this GitHub Pull Request |
| 180 | + <https://github.com/Sylius/Customizations/pull/23>`__. |
| 181 | + |
| 182 | +**1** First we extended our entity ``App\Entity\Shipping\ShippingMethod`` with a new field ``deliveryConditions``. |
| 183 | + |
| 184 | +**2** Next we need extends our factory ``Sylius\Bundle\CoreBundle\Fixture\Factory\ShippingMethodExampleFactory`` with this field |
| 185 | +in ``App\Entity\Factory\ShippingMethodExampleFactory``: |
| 186 | + |
| 187 | +.. code-block:: php |
| 188 | +
|
| 189 | + <?php |
| 190 | +
|
| 191 | + ... |
| 192 | +
|
| 193 | + final class ShippingMethodExampleFactory extends BaseShippingMethodExampleFactory implements ExampleFactoryInterface |
| 194 | + { |
| 195 | + ... |
| 196 | +
|
| 197 | + public function create(array $options = []): ShippingMethodInterface |
| 198 | + { |
| 199 | + /** @var ShippingMethod $shippingMethod */ |
| 200 | + $shippingMethod = parent::create($options); |
| 201 | +
|
| 202 | + // here we protect object if part of our objects don't have new field |
| 203 | + if (!isset($options['deliveryConditions'])) { |
| 204 | + return $shippingMethod; |
| 205 | + } |
| 206 | +
|
| 207 | + foreach ($this->getLocales() as $localeCode) { |
| 208 | + $shippingMethod->setCurrentLocale($localeCode); |
| 209 | + $shippingMethod->setFallbackLocale($localeCode); |
| 210 | +
|
| 211 | + $shippingMethod->setDeliveryConditions($options['deliveryConditions']); |
| 212 | + } |
| 213 | +
|
| 214 | + return $shippingMethod; |
| 215 | + } |
| 216 | +
|
| 217 | + protected function configureOptions(OptionsResolver $resolver): void |
| 218 | + { |
| 219 | + parent::configureOptions($resolver); |
| 220 | +
|
| 221 | + $resolver |
| 222 | + ->setDefault('deliveryConditions', 'some_default_value') |
| 223 | + ->setAllowedTypes('deliveryConditions', ['null', 'string']) |
| 224 | + ; |
| 225 | + } |
| 226 | +
|
| 227 | + private function getLocales(): iterable |
| 228 | + { |
| 229 | + ... |
| 230 | + } |
| 231 | + } |
| 232 | +
|
| 233 | +**3** Now we extended ``Sylius\Bundle\CoreBundle\Fixture\ShippingMethodFixture`` in ``App\Entity\Fixture\ShippingMethodFixture``: |
| 234 | + |
| 235 | +.. code-block:: php |
| 236 | +
|
| 237 | + <?php |
| 238 | +
|
| 239 | + ... |
| 240 | +
|
| 241 | + final class ShippingMethodFixture extends BaseShippingMethodFixture implements FixtureInterface |
| 242 | + { |
| 243 | + public function getName(): string |
| 244 | + { |
| 245 | + return 'shipping_method'; |
| 246 | + } |
| 247 | +
|
| 248 | + protected function configureResourceNode(ArrayNodeDefinition $resourceNode): void |
| 249 | + { |
| 250 | + parent::configureResourceNode($resourceNode); |
| 251 | +
|
| 252 | + $resourceNode |
| 253 | + ->children() |
| 254 | + ->scalarNode('deliveryConditions')->end() |
| 255 | + ; |
| 256 | + } |
| 257 | + } |
| 258 | +
|
| 259 | +**4** Here we create ``config/packages/fixtures.xml`` and override our services: |
| 260 | + |
| 261 | +.. code-block:: xml |
| 262 | +
|
| 263 | + <?xml version="1.0" encoding="UTF-8"?> |
| 264 | +
|
| 265 | + <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> |
| 266 | + <services> |
| 267 | + <defaults public="true" /> |
| 268 | +
|
| 269 | + <service id="sylius.fixture.shipping_method" class="App\Entity\Fixture\ShippingMethodFixture"> |
| 270 | + <argument type="service" id="sylius.manager.shipping_method" /> |
| 271 | + <argument type="service" id="sylius.fixture.example_factory.shipping_method" /> |
| 272 | + <tag name="sylius_fixtures.fixture" /> |
| 273 | + </service> |
| 274 | +
|
| 275 | + <service id="sylius.fixture.example_factory.shipping_method" class="App\Entity\Factory\ShippingMethodExampleFactory"> |
| 276 | + <argument type="service" id="sylius.factory.shipping_method" /> |
| 277 | + <argument type="service" id="sylius.repository.zone" /> |
| 278 | + <argument type="service" id="sylius.repository.shipping_category" /> |
| 279 | + <argument type="service" id="sylius.repository.locale" /> |
| 280 | + <argument type="service" id="sylius.repository.channel" /> |
| 281 | + </service> |
| 282 | + </services> |
| 283 | + </container> |
| 284 | +
|
| 285 | +**5** At the end, only what you have to do is add new ``shipping_method`` in ``config\packages\_sylius.yml`` |
| 286 | + |
| 287 | +.. code-block:: yaml |
| 288 | +
|
| 289 | + sylius_fixtures: |
| 290 | + suites: |
| 291 | + default: |
| 292 | + fixtures: |
| 293 | + ... |
| 294 | + shipping_method: # our new configuration with a new field |
| 295 | + options: |
| 296 | + custom: |
| 297 | + geis: |
| 298 | + code: "geis" |
| 299 | + name: "geis" |
| 300 | + enabled: true |
| 301 | + channels: |
| 302 | + - "PL_WEB" |
| 303 | + deliveryConditions: "delivered" |
| 304 | +
|
| 305 | +Learn more |
| 306 | +########## |
| 307 | + |
| 308 | +* :doc:`FixtureBundle </components_and_bundles/bundles/SyliusFixturesBundle/index>` |
0 commit comments