Skip to content

Commit 381f0d3

Browse files
Merge pull request #399 from sumocoders/meta-alternate-language
Meta alternate language
2 parents cadce74 + 18912b7 commit 381f0d3

File tree

29 files changed

+540
-33
lines changed

29 files changed

+540
-33
lines changed

app/config/config.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,9 @@ services:
276276
arguments:
277277
- "@fork.settings"
278278
- "@fork.cookie"
279+
280+
Common\Doctrine\Repository\MetaRepository:
281+
public: true
282+
factory: ["@doctrine.orm.entity_manager", getRepository]
283+
arguments:
284+
- Common\Doctrine\Entity\Meta

src/Backend/Core/Engine/Base/ActionAdd.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,8 @@ protected function parse(): void
3232
if ($this->form) {
3333
$this->form->parse($this->template);
3434
}
35+
if ($this->meta instanceof Meta) {
36+
$this->template->assign('spoonHreflangFields', $this->meta->getHreflangFields());
37+
}
3538
}
3639
}

src/Backend/Core/Engine/Base/ActionEdit.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,8 @@ protected function parse(): void
5454
if ($this->form) {
5555
$this->form->parse($this->template);
5656
}
57+
if ($this->meta instanceof Meta) {
58+
$this->template->assign('spoonHreflangFields', $this->meta->getHreflangFields());
59+
}
5760
}
5861
}

src/Backend/Core/Engine/Meta.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ class Meta
3030
*/
3131
protected $callback = [];
3232

33+
/**
34+
* The hreflang callback method
35+
*
36+
* @var array
37+
*/
38+
protected $hreflangCallback = [];
39+
3340
/**
3441
* Do we need meta custom
3542
*
@@ -65,6 +72,11 @@ class Meta
6572
*/
6673
protected $url;
6774

75+
/**
76+
* @var array<string, array<string, string>>
77+
*/
78+
private $hreflangFields = [];
79+
6880
/**
6981
* @param Form $form An instance of Form, the elements will be parsed in here.
7082
* @param int $metaId The metaID to load.
@@ -386,6 +398,25 @@ protected function loadForm(): void
386398
$this->form->addHidden('class_name', $this->callback['class']);
387399
$this->form->addHidden('method_name', $this->callback['method']);
388400
$this->form->addHidden('parameters', \SpoonFilter::htmlspecialchars(serialize($this->callback['parameters'])));
401+
402+
if (!empty($this->hreflangCallback)) {
403+
foreach (BackendLanguage::getActiveLanguages() as $language) {
404+
if ($language !== BackendLanguage::getWorkingLanguage()) {
405+
$values = $this->getHreflangValues($language);
406+
$field = $this->form->addDropdown(
407+
'hreflang_' . $language,
408+
$values,
409+
$this->data['data']['hreflang_' . $language] ?? null,
410+
);
411+
$field->setDefaultElement('');
412+
$this->hreflangFields[$language] = [
413+
'language' => $language,
414+
'field' => $field->parse(),
415+
'label' => strtoupper($language),
416+
];
417+
}
418+
}
419+
}
389420
}
390421

391422
/**
@@ -476,6 +507,18 @@ public function setUrlCallback(string $className, string $methodName, array $par
476507
$this->loadForm();
477508
}
478509

510+
public function setHreflangCallback(string $className, string $methodName, array $parameters = []): void
511+
{
512+
$this->hreflangCallback = [
513+
'class' => $className,
514+
'method' => $methodName,
515+
'parameters' => $parameters,
516+
];
517+
518+
// re-load the form
519+
$this->loadForm();
520+
}
521+
479522
/**
480523
* Validates the form
481524
* It checks if there is a value when a checkbox is checked
@@ -555,6 +598,17 @@ public function validate(): void
555598
unset($this->data['data']['canonical_url_overwrite']);
556599
}
557600

601+
if (!empty($this->hreflangCallback)) {
602+
foreach (BackendLanguage::getActiveLanguages() as $language) {
603+
if ($language !== BackendLanguage::getWorkingLanguage()) {
604+
$field = $this->form->getField('hreflang_' . $language);
605+
if ($field->isFilled()) {
606+
$this->data['data']['hreflang_' . $language] = $field->getValue();
607+
}
608+
}
609+
}
610+
}
611+
558612
$this->data['custom'] = $this->custom && $this->form->getField('meta_custom')->isFilled()
559613
? $this->form->getField('meta_custom')->getValue() : null;
560614
$this->data['seo_index'] = $this->form->getField('seo_index')->getValue();
@@ -612,4 +666,38 @@ private function handleOgImage(): ?string
612666

613667
return $filename;
614668
}
669+
670+
/**
671+
* @return array<string, array<string, string>>
672+
*/
673+
public function getHreflangFields(): array
674+
{
675+
return $this->hreflangFields;
676+
}
677+
678+
/**
679+
* @param string $language
680+
*
681+
* @return array<string, string>
682+
*/
683+
private function getHreflangValues(string $language): array
684+
{
685+
$class = $this->hreflangCallback['class'];
686+
$method = $this->hreflangCallback['method'];
687+
$parameters = $this->hreflangCallback['parameters'] ?? [];
688+
689+
// check if the class is a service
690+
if (Model::getContainer()->has($class)) {
691+
$class = Model::getContainer()->get($class);
692+
}
693+
694+
// validate (check if the function exists)
695+
if (!is_callable([$class, $method])) {
696+
throw new Exception('The hreflang callback-method doesn\'t exist.');
697+
}
698+
699+
array_unshift($parameters, $language);
700+
701+
return call_user_func_array([$class, $method], $parameters);
702+
}
615703
}

src/Backend/Core/Layout/Templates/FormLayout.html.twig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@
247247
</div>
248248
</div>
249249
{# og:image end #}
250+
{% if form.hreflang is defined %}
251+
<div class="col-md-12">
252+
{{ form_row(form.hreflang) }}
253+
</div>
254+
{% endif %}
250255
<input type="hidden" id="{{ customId }}" value="{{ custom_meta_tags ? '1' : '0' }}">
251256
<input type="hidden" id="{{ classId }}" value="{{ generate_url_callback_class }}">
252257
<input type="hidden" id="{{ methodId }}" value="{{ generate_url_callback_method }}">

src/Backend/Form/EventListener/AddMetaSubscriber.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,28 @@ class AddMetaSubscriber implements EventSubscriberInterface
4545
*/
4646
private $moduleForUrl;
4747

48+
private ?string $hreflangRepositoryClass;
49+
50+
private ?string $hreflangRepositoryMethod;
51+
4852
public function __construct(
4953
string $moduleForUrl,
5054
string $actionForUrl,
5155
string $generateUrlCallbackClass,
5256
string $generateUrlCallbackMethod,
5357
array $generateUrlCallbackParameterMethods,
54-
string $baseFieldName = 'title'
58+
string $baseFieldName = 'title',
59+
?string $hreflangRepositoryClass = null,
60+
?string $hreflangRepositoryMethod = null
5561
) {
5662
$this->moduleForUrl = $moduleForUrl;
5763
$this->actionForUrl = $actionForUrl;
5864
$this->generateUrlCallbackClass = $generateUrlCallbackClass;
5965
$this->generateUrlCallbackMethod = $generateUrlCallbackMethod;
6066
$this->generateUrlCallbackParameterMethods = $generateUrlCallbackParameterMethods;
6167
$this->baseFieldName = $baseFieldName;
68+
$this->hreflangRepositoryClass = $hreflangRepositoryClass;
69+
$this->hreflangRepositoryMethod = $hreflangRepositoryMethod;
6270
}
6371

6472
public static function getSubscribedEvents(): array
@@ -79,6 +87,8 @@ public function preSetData(FormEvent $event)
7987
'generate_url_callback_class' => $this->generateUrlCallbackClass,
8088
'generate_url_callback_method' => $this->generateUrlCallbackMethod,
8189
'generate_url_callback_parameters' => $this->buildCallbackParameters($event),
90+
'hreflang_repository_class' => $this->hreflangRepositoryClass,
91+
'hreflang_repository_method' => $this->hreflangRepositoryMethod,
8292
]
8393
);
8494
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Backend\Form\Type;
4+
5+
use Backend\Core\Engine\Model;
6+
use Backend\Core\Language\Locale;
7+
use RuntimeException;
8+
use Symfony\Component\Form\AbstractType;
9+
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
10+
use Symfony\Component\Form\FormBuilderInterface;
11+
use Symfony\Component\Form\FormEvent;
12+
use Symfony\Component\Form\FormEvents;
13+
use Symfony\Component\OptionsResolver\OptionsResolver;
14+
15+
final class HreflangType extends AbstractType
16+
{
17+
public function buildForm(FormBuilderInterface $builder, array $options): void
18+
{
19+
if (!Model::has($repository = $options['repository_class'])) {
20+
throw new RuntimeException(sprintf('Repository class "%s" not found.', $repository));
21+
}
22+
23+
$repository = Model::get($options['repository_class']);
24+
$method = $options['repository_method'];
25+
26+
if (!method_exists($repository, $method)) {
27+
throw new RuntimeException(sprintf('Repository method "%s" not found in "%s".', $method, $repository));
28+
}
29+
30+
$builder->addEventListener(
31+
FormEvents::POST_SET_DATA,
32+
function (FormEvent $event) use ($repository, $method) {
33+
$language = $event->getData()['language'];
34+
$choices = $repository->$method(Locale::fromString($language));
35+
36+
$event->getForm()->add(
37+
'entity',
38+
ChoiceType::class,
39+
[
40+
'label' => strtoupper($language),
41+
'choices' => array_column($choices, 'id', 'title'),
42+
'placeholder' => '',
43+
'attr' => [
44+
'data-role' => 'tom-select',
45+
],
46+
]
47+
);
48+
}
49+
);
50+
}
51+
52+
public function configureOptions(OptionsResolver $resolver): void
53+
{
54+
$resolver->setRequired('repository_class');
55+
$resolver->setRequired('repository_method');
56+
}
57+
}

src/Backend/Form/Type/MetaType.php

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace Backend\Form\Type;
44

5+
use Backend\Core\Language\Language;
56
use Common\Doctrine\Entity\Meta;
67
use Common\Doctrine\Repository\MetaRepository;
78
use Common\Doctrine\ValueObject\SEOFollow;
89
use Common\Doctrine\ValueObject\SEOIndex;
10+
use Common\Form\CollectionType;
911
use SpoonFilter;
1012
use Symfony\Component\Form\AbstractType;
1113
use Symfony\Component\Form\CallbackTransformer;
@@ -130,6 +132,22 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
130132
)
131133
->addEventListener(FormEvents::SUBMIT, $this->getSubmitEventFunction($options['base_field_name']));
132134

135+
if ($options['hreflang_repository_class'] && $options['hreflang_repository_method']) {
136+
$builder->add(
137+
'hreflang',
138+
CollectionType::class,
139+
[
140+
'label' => 'lbl.Languages',
141+
'required' => false,
142+
'entry_type' => HreflangType::class,
143+
'entry_options' => [
144+
'repository_class' => $options['hreflang_repository_class'],
145+
'repository_method' => $options['hreflang_repository_method'],
146+
],
147+
]
148+
);
149+
}
150+
133151
if ($options['custom_meta_tags']) {
134152
$builder->add(
135153
'custom',
@@ -256,13 +274,35 @@ private function getMetaTransformFunction(): callable
256274
{
257275
return function ($meta) {
258276
if (!$meta instanceof Meta) {
277+
$hreflang = [];
278+
foreach (Language::getActiveLanguages() as $activeLanguage) {
279+
if ($activeLanguage !== Language::getWorkingLanguage()) {
280+
$hreflang[$activeLanguage] = [
281+
'language' => $activeLanguage,
282+
'entity' => null,
283+
];
284+
}
285+
}
286+
259287
return [
260288
'SEOIndex' => SEOIndex::none(),
261289
'SEOFollow' => SEOFollow::none(),
290+
'hreflang' => $hreflang,
262291
];
263292
}
264293

265294
$this->meta[$meta->getId()] = $meta;
295+
$data = $meta->getData();
296+
297+
$hreflang = [];
298+
foreach (Language::getActiveLanguages() as $activeLanguage) {
299+
if ($activeLanguage !== Language::getWorkingLanguage()) {
300+
$hreflang[$activeLanguage] = [
301+
'language' => $activeLanguage,
302+
'entity' => $data['hreflang_' . $activeLanguage] ?? null,
303+
];
304+
}
305+
}
266306

267307
return [
268308
'id' => $meta->getId(),
@@ -279,6 +319,7 @@ private function getMetaTransformFunction(): callable
279319
'canonicalUrlOverwrite' => $meta->isCanonicalUrlOverwrite(),
280320
'SEOIndex' => $meta->getSEOIndex() ?? SEOIndex::none(),
281321
'SEOFollow' => $meta->getSEOFollow() ?? SEOFollow::none(),
322+
'hreflang' => $hreflang,
282323
];
283324
};
284325
}
@@ -288,6 +329,15 @@ private function getMetaReverseTransformFunction(): callable
288329
return function ($metaData) {
289330
$metaId = $metaData['id'] === null ? null : (int) $metaData['id'];
290331

332+
$data = [];
333+
if (!empty($metaData['hreflang'])) {
334+
foreach ($metaData['hreflang'] as $hreflang) {
335+
if ($hreflang['entity'] !== null) {
336+
$data['hreflang_' . $hreflang['language']] = $hreflang['entity'];
337+
}
338+
}
339+
}
340+
291341
if ($metaId === null || !$this->meta[$metaId] instanceof Meta) {
292342
return new Meta(
293343
$metaData['keywords'],
@@ -303,7 +353,7 @@ private function getMetaReverseTransformFunction(): callable
303353
$metaData['custom'] ?? null,
304354
SEOFollow::fromString((string) $metaData['SEOFollow']),
305355
SEOIndex::fromString((string) $metaData['SEOIndex']),
306-
[],
356+
$data,
307357
$metaData['ogImage'],
308358
$metaData['deleteOgImage'],
309359
$metaId
@@ -324,7 +374,7 @@ private function getMetaReverseTransformFunction(): callable
324374
array_key_exists('custom', $metaData) ? $metaData['custom'] : null,
325375
SEOFollow::fromString((string) $metaData['SEOFollow']),
326376
SEOIndex::fromString((string) $metaData['SEOIndex']),
327-
[],
377+
$data,
328378
$metaData['ogImage'],
329379
$metaData['deleteOgImage']
330380
);
@@ -352,6 +402,8 @@ public function configureOptions(OptionsResolver $resolver): void
352402
'custom_meta_tags' => false,
353403
'generated_url_selector' => '#generatedUrl',
354404
'generate_url_callback_parameters' => [],
405+
'hreflang_repository_class' => null,
406+
'hreflang_repository_method' => null,
355407
]
356408
);
357409
}

src/Backend/Modules/Blog/Actions/Add.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ private function loadForm(): void
7777

7878
// meta
7979
$this->meta = new BackendMeta($this->form, null, 'title', true);
80+
$this->meta->setHreflangCallback(BackendBlogModel::class, 'getHrefLangValues');
8081
}
8182

8283
protected function parse(): void

0 commit comments

Comments
 (0)