Completed
Push — master ( d81c19...f57266 )
by Kamil
20s
created

Fixture/Factory/AddressExampleFactory.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Bundle\CoreBundle\Fixture\Factory;
15
16
use Doctrine\Common\Collections\Collection;
17
use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption;
18
use Sylius\Component\Addressing\Model\Country;
19
use Sylius\Component\Addressing\Model\ProvinceInterface;
20
use Sylius\Component\Core\Model\AddressInterface;
21
use Sylius\Component\Core\Model\CustomerInterface;
22
use Sylius\Component\Resource\Factory\FactoryInterface;
23
use Sylius\Component\Resource\Repository\RepositoryInterface;
24
use Symfony\Component\OptionsResolver\Options;
25
use Symfony\Component\OptionsResolver\OptionsResolver;
26
use Webmozart\Assert\Assert;
27
28
class AddressExampleFactory extends AbstractExampleFactory
29
{
30
    /**
31
     * @var FactoryInterface
32
     */
33
    private $addressFactory;
34
35
    /**
36
     * @var RepositoryInterface
37
     */
38
    private $countryRepository;
39
40
    /**
41
     * @var RepositoryInterface
42
     */
43
    private $customerRepository;
44
45
    /**
46
     * @var \Faker\Generator
47
     */
48
    private $faker;
49
50
    /**
51
     * @var OptionsResolver
52
     */
53
    private $optionsResolver;
54
55
    /**
56
     * @param FactoryInterface $addressFactory
57
     * @param RepositoryInterface $countryRepository
58
     * @param RepositoryInterface $customerRepository
59
     */
60
    public function __construct(
61
        FactoryInterface $addressFactory,
62
        RepositoryInterface $countryRepository,
63
        RepositoryInterface $customerRepository
64
    ) {
65
        $this->addressFactory = $addressFactory;
66
        $this->countryRepository = $countryRepository;
67
        $this->customerRepository = $customerRepository;
68
69
        $this->faker = \Faker\Factory::create();
70
        $this->optionsResolver = new OptionsResolver();
71
72
        $this->configureOptions($this->optionsResolver);
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    protected function configureOptions(OptionsResolver $resolver): void
79
    {
80
        $resolver
81
            ->setDefault('first_name', function (Options $options): string {
82
                return $this->faker->firstName;
83
            })
84
            ->setDefault('last_name', function (Options $options): string {
85
                return $this->faker->lastName;
86
            })
87
            ->setDefault('phone_number', function (Options $options): ?string {
88
                return random_int(1, 100) > 50 ? $this->faker->phoneNumber : null;
89
            })
90
            ->setDefault('company', function (Options $options): ?string {
91
                return random_int(1, 100) > 50 ? $this->faker->company : null;
92
            })
93
            ->setDefault('street', function (Options $options): string {
94
                return $this->faker->streetAddress;
95
            })
96
            ->setDefault('city', function (Options $options): string {
97
                return $this->faker->city;
98
            })
99
            ->setDefault('postcode', function (Options $options): string {
100
                return $this->faker->postcode;
101
            })
102
            ->setDefault('country_code', function (Options $options): string {
103
                $countries = $this->countryRepository->findAll();
104
                shuffle($countries);
105
106
                return array_pop($countries)->getCode();
107
            })
108
            ->setAllowedTypes('country_code', ['string'])
109
            ->setDefault('province_name', null)
110
            ->setAllowedTypes('province_name', ['null', 'string'])
111
            ->setDefault('province_code', null)
112
            ->setAllowedTypes('province_code', ['null', 'string'])
113
            ->setDefault('customer', LazyOption::randomOne($this->customerRepository))
114
            ->setAllowedTypes('customer', ['string', CustomerInterface::class, 'null'])
115
            ->setNormalizer('customer', LazyOption::findOneBy($this->customerRepository, 'email'))
116
        ;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function create(array $options = []): AddressInterface
123
    {
124
        $options = $this->optionsResolver->resolve($options);
125
126
        /** @var AddressInterface $address */
127
        $address = $this->addressFactory->createNew();
128
        $address->setFirstName($options['first_name']);
129
        $address->setLastName($options['last_name']);
130
        $address->setPhoneNumber($options['phone_number']);
131
        $address->setCompany($options['company']);
132
        $address->setStreet($options['street']);
133
        $address->setCity($options['city']);
134
        $address->setPostcode($options['postcode']);
135
136
        $this->assertCountryCodeIsValid($options['country_code']);
137
        $address->setCountryCode($options['country_code']);
138
139
        $this->resolveCountryProvince($options, $address);
140
141
        if (isset($options['customer'])) {
142
            $options['customer']->addAddress($address);
143
        }
144
145
        return $address;
146
    }
147
148
    /**
149
     * @param string $code
150
     *
151
     * @throws \InvalidArgumentException
152
     */
153
    private function assertCountryCodeIsValid(string $code): void
154
    {
155
        $country = $this->countryRepository->findOneBy(['code' => $code]);
156
        Assert::notNull($country);
157
    }
158
159
    /**
160
     * @param string $provinceCode
161
     * @param string $countryCode
162
     *
163
     * @throws \InvalidArgumentException
164
     */
165
    private function assertProvinceCodeIsValid(string $provinceCode, string $countryCode): void
166
    {
167
        $country = $this->countryRepository->findOneBy(['code' => $countryCode]);
168
169
        /** @var ProvinceInterface $province */
170
        foreach ($country->getProvinces() as $province) {
171
            if ($province->getCode() === $provinceCode) {
172
                return;
173
            }
174
        }
175
176
        throw new \InvalidArgumentException(sprintf('Provided province code is not valid for "%s"', $country->getName()));
177
    }
178
179
    /**
180
     * @param array $options
181
     * @param AddressInterface $address
182
     */
183
    private function provideProvince(array $options, AddressInterface $address): void
184
    {
185
        /** @var Country $country */
186
        $country = $this->countryRepository->findOneBy(['code' => $options['country_code']]);
187
188
        if ($country->hasProvinces()) {
189
            $address->setProvinceCode($this->getProvinceCode($country->getProvinces(), $options['province_name']));
0 ignored issues
show
It seems like $country->getProvinces() targeting Sylius\Component\Address...Country::getProvinces() can also be of type array<integer,object<Syl...del\ProvinceInterface>>; however, Sylius\Bundle\CoreBundle...tory::getProvinceCode() does only seem to accept object<Doctrine\Common\Collections\Collection>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
190
191
            return;
192
        }
193
194
        $address->setProvinceName($options['province_name']);
195
    }
196
197
    /**
198
     * @param Collection $provinces
199
     * @param string $provinceName
200
     *
201
     * @return string
202
     *
203
     * @throws \InvalidArgumentException
204
     */
205
    private function getProvinceCode(Collection $provinces, string $provinceName): string
206
    {
207
        /** @var ProvinceInterface $province */
208
        foreach ($provinces as $province) {
209
            if ($province->getName() === $provinceName) {
210
                return $province->getCode();
211
            }
212
        }
213
214
        throw new \InvalidArgumentException(sprintf('Country has defined provinces, but %s is not one of them', $provinceName));
215
    }
216
217
    /**
218
     * @param array $options
219
     * @param AddressInterface $address
220
     */
221
    private function resolveCountryProvince(array $options, AddressInterface $address): void
222
    {
223
        if (null !== $options['province_code']) {
224
            $this->assertProvinceCodeIsValid($options['province_code'], $options['country_code']);
225
            $address->setProvinceCode($options['province_code']);
226
227
            return;
228
        }
229
230
        if (null !== $options['province_name']) {
231
            $this->provideProvince($options, $address);
232
        }
233
    }
234
}
235