1 | <?php |
||||||
2 | /** |
||||||
3 | * SEOmatic plugin for Craft CMS |
||||||
4 | * |
||||||
5 | * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful, |
||||||
6 | * and flexible |
||||||
7 | * |
||||||
8 | * @link https://nystudio107.com |
||||||
9 | * @copyright Copyright (c) 2019 nystudio107 |
||||||
10 | */ |
||||||
11 | |||||||
12 | namespace nystudio107\seomatic\seoelements; |
||||||
13 | |||||||
14 | use Craft; |
||||||
15 | use craft\base\ElementInterface; |
||||||
0 ignored issues
–
show
|
|||||||
16 | use craft\base\Model; |
||||||
17 | use craft\elements\db\ElementQueryInterface; |
||||||
18 | use craft\events\DefineHtmlEvent; |
||||||
19 | use craft\models\Site; |
||||||
20 | use Exception; |
||||||
21 | use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset; |
||||||
22 | use nystudio107\seomatic\base\SeoElementInterface; |
||||||
23 | use nystudio107\seomatic\helpers\ArrayHelper; |
||||||
24 | use nystudio107\seomatic\helpers\Config as ConfigHelper; |
||||||
25 | use nystudio107\seomatic\helpers\PluginTemplate; |
||||||
26 | use nystudio107\seomatic\integrations\campaign\behaviors\CampaignBehavior; |
||||||
27 | use nystudio107\seomatic\models\MetaBundle; |
||||||
28 | use nystudio107\seomatic\Seomatic; |
||||||
29 | use putyourlightson\campaign\Campaign; |
||||||
30 | use putyourlightson\campaign\elements\CampaignElement; |
||||||
31 | use putyourlightson\campaign\events\CampaignTypeEvent; |
||||||
32 | use putyourlightson\campaign\models\CampaignTypeModel; |
||||||
33 | use putyourlightson\campaign\services\CampaignTypesService; |
||||||
34 | use yii\base\Event; |
||||||
35 | use yii\base\InvalidConfigException; |
||||||
36 | |||||||
37 | /** |
||||||
38 | * @author nystudio107 |
||||||
39 | * @package Seomatic |
||||||
40 | * @since 3.2.0 |
||||||
41 | */ |
||||||
42 | class SeoCampaign implements SeoElementInterface |
||||||
43 | { |
||||||
44 | // Constants |
||||||
45 | // ========================================================================= |
||||||
46 | |||||||
47 | public const META_BUNDLE_TYPE = 'campaign'; |
||||||
48 | public const ELEMENT_CLASSES = [ |
||||||
49 | CampaignElement::class, |
||||||
50 | ]; |
||||||
51 | public const REQUIRED_PLUGIN_HANDLE = 'campaign'; |
||||||
52 | public const CONFIG_FILE_PATH = 'campaignmeta/Bundle'; |
||||||
53 | |||||||
54 | // Public Static Methods |
||||||
55 | // ========================================================================= |
||||||
56 | |||||||
57 | /** |
||||||
58 | * Return the sourceBundleType for that this SeoElement handles |
||||||
59 | * |
||||||
60 | * @return string |
||||||
61 | */ |
||||||
62 | public static function getMetaBundleType(): string |
||||||
63 | { |
||||||
64 | return self::META_BUNDLE_TYPE; |
||||||
65 | } |
||||||
66 | |||||||
67 | /** |
||||||
68 | * Returns an array of the element classes that are handled by this SeoElement |
||||||
69 | * |
||||||
70 | * @return array |
||||||
71 | */ |
||||||
72 | public static function getElementClasses(): array |
||||||
73 | { |
||||||
74 | return self::ELEMENT_CLASSES; |
||||||
75 | } |
||||||
76 | |||||||
77 | /** |
||||||
78 | * Return the refHandle (e.g.: `entry` or `category`) for the SeoElement |
||||||
79 | * |
||||||
80 | * @return string |
||||||
81 | */ |
||||||
82 | public static function getElementRefHandle(): string |
||||||
83 | { |
||||||
84 | return CampaignElement::refHandle() ?? 'campaign'; |
||||||
85 | } |
||||||
86 | |||||||
87 | /** |
||||||
88 | * Return the handle to a required plugin for this SeoElement type |
||||||
89 | * |
||||||
90 | * @return null|string |
||||||
91 | */ |
||||||
92 | public static function getRequiredPluginHandle() |
||||||
93 | { |
||||||
94 | return self::REQUIRED_PLUGIN_HANDLE; |
||||||
95 | } |
||||||
96 | |||||||
97 | /** |
||||||
98 | * Install any event handlers for this SeoElement type |
||||||
99 | */ |
||||||
100 | public static function installEventHandlers() |
||||||
101 | { |
||||||
102 | $request = Craft::$app->getRequest(); |
||||||
103 | |||||||
104 | // Install for all requests |
||||||
105 | Event::on( |
||||||
106 | CampaignTypesService::class, |
||||||
107 | CampaignTypesService::EVENT_AFTER_SAVE_CAMPAIGN_TYPE, |
||||||
108 | function(CampaignTypeEvent $event) { |
||||||
0 ignored issues
–
show
The parameter
$event is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
109 | Craft::debug( |
||||||
110 | 'CampaignTypesService::EVENT_AFTER_SAVE_CAMPAIGN_TYPE', |
||||||
111 | __METHOD__ |
||||||
112 | ); |
||||||
113 | Seomatic::$plugin->metaBundles->resaveMetaBundles(self::META_BUNDLE_TYPE); |
||||||
114 | } |
||||||
115 | ); |
||||||
116 | Event::on( |
||||||
117 | CampaignTypesService::class, |
||||||
118 | CampaignTypesService::EVENT_AFTER_DELETE_CAMPAIGN_TYPE, |
||||||
119 | function(CampaignTypeEvent $event) { |
||||||
0 ignored issues
–
show
The parameter
$event is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
120 | Craft::debug( |
||||||
121 | 'CampaignTypesService::EVENT_AFTER_DELETE_CAMPAIGN_TYPE', |
||||||
122 | __METHOD__ |
||||||
123 | ); |
||||||
124 | Seomatic::$plugin->metaBundles->resaveMetaBundles(self::META_BUNDLE_TYPE); |
||||||
125 | } |
||||||
126 | ); |
||||||
127 | |||||||
128 | // Install for all non-console requests |
||||||
129 | if (!$request->getIsConsoleRequest()) { |
||||||
130 | // Handler: CampaignTypesService::EVENT_AFTER_SAVE_CAMPAIGN_TYPE |
||||||
131 | Event::on( |
||||||
132 | CampaignTypesService::class, |
||||||
133 | CampaignTypesService::EVENT_AFTER_SAVE_CAMPAIGN_TYPE, |
||||||
134 | function(CampaignTypeEvent $event) { |
||||||
135 | Craft::debug( |
||||||
136 | 'CampaignTypesService::EVENT_AFTER_SAVE_CAMPAIGN_TYPE', |
||||||
137 | __METHOD__ |
||||||
138 | ); |
||||||
139 | if ($event->campaignType !== null && $event->campaignType->id !== null) { |
||||||
140 | Seomatic::$plugin->metaBundles->invalidateMetaBundleById( |
||||||
141 | SeoCampaign::getMetaBundleType(), |
||||||
142 | $event->campaignType->id, |
||||||
143 | $event->isNew |
||||||
144 | ); |
||||||
145 | // Create the meta bundles for this campaign type if it's new |
||||||
146 | if ($event->isNew) { |
||||||
147 | SeoCampaign::createContentMetaBundle($event->campaignType); |
||||||
148 | Seomatic::$plugin->sitemaps->submitSitemapIndex(); |
||||||
149 | } |
||||||
150 | } |
||||||
151 | } |
||||||
152 | ); |
||||||
153 | // Handler: CampaignTypesService::EVENT_AFTER_DELETE_CAMPAIGN_TYPE |
||||||
154 | Event::on( |
||||||
155 | CampaignTypesService::class, |
||||||
156 | CampaignTypesService::EVENT_AFTER_DELETE_CAMPAIGN_TYPE, |
||||||
157 | function(CampaignTypeEvent $event) { |
||||||
158 | Craft::debug( |
||||||
159 | 'CampaignTypesService::EVENT_AFTER_DELETE_CAMPAIGN_TYPE', |
||||||
160 | __METHOD__ |
||||||
161 | ); |
||||||
162 | if ($event->campaignType !== null && $event->campaignType->id !== null) { |
||||||
163 | Seomatic::$plugin->metaBundles->invalidateMetaBundleById( |
||||||
164 | SeoCampaign::getMetaBundleType(), |
||||||
165 | $event->campaignType->id, |
||||||
166 | false |
||||||
167 | ); |
||||||
168 | // Delete the meta bundles for this campaign type |
||||||
169 | Seomatic::$plugin->metaBundles->deleteMetaBundleBySourceId( |
||||||
170 | SeoCampaign::getMetaBundleType(), |
||||||
171 | $event->campaignType->id |
||||||
172 | ); |
||||||
173 | } |
||||||
174 | } |
||||||
175 | ); |
||||||
176 | } |
||||||
177 | |||||||
178 | // Install only for non-console site requests |
||||||
179 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||||
180 | } |
||||||
181 | |||||||
182 | // Handler: Entry::EVENT_DEFINE_SIDEBAR_HTML |
||||||
183 | Event::on( |
||||||
184 | CampaignElement::class, |
||||||
185 | CampaignElement::EVENT_DEFINE_SIDEBAR_HTML, |
||||||
186 | static function(DefineHtmlEvent $event) { |
||||||
187 | Craft::debug( |
||||||
188 | 'CampaignElement::EVENT_DEFINE_SIDEBAR_HTML', |
||||||
189 | __METHOD__ |
||||||
190 | ); |
||||||
191 | $html = ''; |
||||||
192 | Seomatic::$view->registerAssetBundle(SeomaticAsset::class); |
||||||
0 ignored issues
–
show
The method
registerAssetBundle() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
193 | /** @var CampaignElement $campaign */ |
||||||
194 | $campaign = $event->sender ?? null; |
||||||
195 | if ($campaign !== null && $campaign->uri !== null) { |
||||||
196 | Seomatic::$plugin->metaContainers->previewMetaContainers($campaign->uri, $campaign->siteId, true); |
||||||
197 | // Render our preview sidebar template |
||||||
198 | if (Seomatic::$settings->displayPreviewSidebar && Seomatic::$matchedElement) { |
||||||
199 | $html .= PluginTemplate::renderPluginTemplate('_sidebars/campaign-preview.twig'); |
||||||
200 | } |
||||||
201 | // Render our analysis sidebar template |
||||||
202 | // @TODO: This will be added an upcoming 'pro' edition |
||||||
203 | // if (Seomatic::$settings->displayAnalysisSidebar && Seomatic::$matchedElement) { |
||||||
204 | // $html .= PluginTemplate::renderPluginTemplate('_sidebars/campaign-analysis.twig'); |
||||||
205 | // } |
||||||
206 | } |
||||||
207 | $event->html .= $html; |
||||||
208 | } |
||||||
209 | ); |
||||||
210 | } |
||||||
211 | |||||||
212 | /** |
||||||
213 | * Return an ElementQuery for the sitemap elements for the given MetaBundle |
||||||
214 | * |
||||||
215 | * @param MetaBundle $metaBundle |
||||||
216 | * |
||||||
217 | * @return ElementQueryInterface |
||||||
218 | */ |
||||||
219 | public static function sitemapElementsQuery(MetaBundle $metaBundle): ElementQueryInterface |
||||||
220 | { |
||||||
221 | $query = CampaignElement::find() |
||||||
222 | ->campaignType($metaBundle->sourceHandle) |
||||||
223 | ->siteId($metaBundle->sourceSiteId) |
||||||
224 | ->limit($metaBundle->metaSitemapVars->sitemapLimit); |
||||||
225 | |||||||
226 | return $query; |
||||||
227 | } |
||||||
228 | |||||||
229 | /** |
||||||
230 | * Return an ElementInterface for the sitemap alt element for the given MetaBundle |
||||||
231 | * and Element ID |
||||||
232 | * |
||||||
233 | * @param MetaBundle $metaBundle |
||||||
234 | * @param int $elementId |
||||||
235 | * @param int $siteId |
||||||
236 | * |
||||||
237 | * @return null|ElementInterface |
||||||
238 | */ |
||||||
239 | public static function sitemapAltElement( |
||||||
240 | MetaBundle $metaBundle, |
||||||
241 | int $elementId, |
||||||
242 | int $siteId, |
||||||
243 | ) { |
||||||
244 | return CampaignElement::find() |
||||||
0 ignored issues
–
show
|
|||||||
245 | ->campaignType($metaBundle->sourceHandle) |
||||||
246 | ->id($elementId) |
||||||
247 | ->siteId($siteId) |
||||||
248 | ->limit(1) |
||||||
249 | ->one(); |
||||||
250 | } |
||||||
251 | |||||||
252 | /** |
||||||
253 | * Return a preview URI for a given $sourceHandle and $siteId |
||||||
254 | * This just returns the first element |
||||||
255 | * |
||||||
256 | * @param string $sourceHandle |
||||||
257 | * @param int|null $siteId |
||||||
258 | * @param int|string|null $typeId |
||||||
259 | * |
||||||
260 | * @return ?string |
||||||
261 | */ |
||||||
262 | public static function previewUri(string $sourceHandle, $siteId, $typeId = null): ?string |
||||||
263 | { |
||||||
264 | $uri = null; |
||||||
265 | $element = CampaignElement::find() |
||||||
266 | ->campaignType($sourceHandle) |
||||||
267 | ->siteId($siteId) |
||||||
268 | ->one(); |
||||||
269 | if ($element) { |
||||||
270 | $uri = $element->uri; |
||||||
271 | } |
||||||
272 | |||||||
273 | return $uri; |
||||||
274 | } |
||||||
275 | |||||||
276 | /** |
||||||
277 | * Return an array of FieldLayouts from the $sourceHandle |
||||||
278 | * |
||||||
279 | * @param string $sourceHandle |
||||||
280 | * @param int|string|null $typeId |
||||||
281 | * |
||||||
282 | * @return array |
||||||
283 | */ |
||||||
284 | public static function fieldLayouts(string $sourceHandle, $typeId = null): array |
||||||
285 | { |
||||||
286 | $layouts = []; |
||||||
287 | try { |
||||||
288 | $campaignType = Campaign::$plugin->campaignTypes->getCampaignTypeByHandle($sourceHandle); |
||||||
289 | if ($campaignType) { |
||||||
290 | $layouts[] = $campaignType->getFieldLayout(); |
||||||
291 | } |
||||||
292 | } catch (Exception $e) { |
||||||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||||||
293 | } |
||||||
294 | |||||||
295 | return $layouts; |
||||||
296 | } |
||||||
297 | |||||||
298 | /** |
||||||
299 | * Return the (entry) type menu as a $id => $name associative array |
||||||
300 | * |
||||||
301 | * @param string $sourceHandle |
||||||
302 | * |
||||||
303 | * @return array |
||||||
304 | */ |
||||||
305 | public static function typeMenuFromHandle(string $sourceHandle): array |
||||||
306 | { |
||||||
307 | return []; |
||||||
308 | } |
||||||
309 | |||||||
310 | /** |
||||||
311 | * Return the source model of the given $sourceId |
||||||
312 | * |
||||||
313 | * @param int $sourceId |
||||||
314 | * |
||||||
315 | * @return CampaignTypeModel|null |
||||||
316 | */ |
||||||
317 | public static function sourceModelFromId(int $sourceId) |
||||||
318 | { |
||||||
319 | // Attach a behavior to implement ::getSiteSettings() which the CampaignTypeModel lacks |
||||||
320 | $sourceModel = Campaign::$plugin->campaignTypes->getCampaignTypeById($sourceId); |
||||||
321 | if ($sourceModel) { |
||||||
322 | $sourceModel->attachBehavior('SEOmaticCampaignBehavior', CampaignBehavior::class); |
||||||
323 | } |
||||||
324 | |||||||
325 | return $sourceModel; |
||||||
326 | } |
||||||
327 | |||||||
328 | /** |
||||||
329 | * Return the source model of the given $sourceId |
||||||
330 | * |
||||||
331 | * @param string $sourceHandle |
||||||
332 | * |
||||||
333 | * @return CampaignTypeModel|null |
||||||
334 | */ |
||||||
335 | public static function sourceModelFromHandle(string $sourceHandle) |
||||||
336 | { |
||||||
337 | // Attach a behavior to implement ::getSiteSettings() which the CampaignTypeModel lacks |
||||||
338 | $sourceModel = Campaign::$plugin->campaignTypes->getCampaignTypeByHandle($sourceHandle); |
||||||
339 | if ($sourceModel) { |
||||||
340 | $sourceModel->attachBehavior('SEOmaticCampaignBehavior', CampaignBehavior::class); |
||||||
341 | } |
||||||
342 | |||||||
343 | return $sourceModel; |
||||||
344 | } |
||||||
345 | |||||||
346 | /** |
||||||
347 | * Return the most recently updated Element from a given source model |
||||||
348 | * |
||||||
349 | * @param Model $sourceModel |
||||||
350 | * @param int $sourceSiteId |
||||||
351 | * |
||||||
352 | * @return null|ElementInterface |
||||||
353 | */ |
||||||
354 | public static function mostRecentElement(Model $sourceModel, int $sourceSiteId) |
||||||
355 | { |
||||||
356 | /** @var CampaignTypeModel $sourceModel */ |
||||||
357 | return CampaignElement::find() |
||||||
0 ignored issues
–
show
|
|||||||
358 | ->campaignType($sourceModel->handle) |
||||||
359 | ->siteId($sourceSiteId) |
||||||
360 | ->limit(1) |
||||||
361 | ->orderBy(['elements.dateUpdated' => SORT_DESC]) |
||||||
362 | ->one(); |
||||||
363 | } |
||||||
364 | |||||||
365 | /** |
||||||
366 | * Return the path to the config file directory |
||||||
367 | * |
||||||
368 | * @return string |
||||||
369 | */ |
||||||
370 | public static function configFilePath(): string |
||||||
371 | { |
||||||
372 | return self::CONFIG_FILE_PATH; |
||||||
373 | } |
||||||
374 | |||||||
375 | /** |
||||||
376 | * Return a meta bundle config array for the given $sourceModel |
||||||
377 | * |
||||||
378 | * @param Model $sourceModel |
||||||
379 | * |
||||||
380 | * @return array |
||||||
381 | */ |
||||||
382 | public static function metaBundleConfig(Model $sourceModel): array |
||||||
383 | { |
||||||
384 | /** @var CampaignTypeModel $sourceModel */ |
||||||
385 | return ArrayHelper::merge( |
||||||
386 | ConfigHelper::getConfigFromFile(self::configFilePath()), |
||||||
387 | [ |
||||||
388 | 'sourceId' => $sourceModel->id, |
||||||
389 | 'sourceName' => (string)$sourceModel->name, |
||||||
390 | 'sourceHandle' => $sourceModel->handle, |
||||||
391 | ] |
||||||
392 | ); |
||||||
393 | } |
||||||
394 | |||||||
395 | /** |
||||||
396 | * Return the source id from the $element |
||||||
397 | * |
||||||
398 | * @param ElementInterface $element |
||||||
399 | * |
||||||
400 | * @return int|null |
||||||
401 | */ |
||||||
402 | public static function sourceIdFromElement(ElementInterface $element) |
||||||
403 | { |
||||||
404 | /** @var CampaignElement $element */ |
||||||
405 | return $element->campaignTypeId; |
||||||
406 | } |
||||||
407 | |||||||
408 | /** |
||||||
409 | * Return the (entry) type id from the $element |
||||||
410 | * |
||||||
411 | * @param ElementInterface $element |
||||||
412 | * |
||||||
413 | * @return int|null |
||||||
414 | */ |
||||||
415 | public static function typeIdFromElement(ElementInterface $element) |
||||||
416 | { |
||||||
417 | return null; |
||||||
418 | } |
||||||
419 | |||||||
420 | /** |
||||||
421 | * Return the source handle from the $element |
||||||
422 | * |
||||||
423 | * @param ElementInterface $element |
||||||
424 | * |
||||||
425 | * @return string|null |
||||||
426 | */ |
||||||
427 | public static function sourceHandleFromElement(ElementInterface $element) |
||||||
428 | { |
||||||
429 | $sourceHandle = ''; |
||||||
0 ignored issues
–
show
|
|||||||
430 | /** @var CampaignElement $element */ |
||||||
431 | try { |
||||||
432 | $sourceHandle = $element->getCampaignType()->handle; |
||||||
433 | } catch (InvalidConfigException $e) { |
||||||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||||||
434 | } |
||||||
435 | |||||||
436 | return $sourceHandle; |
||||||
437 | } |
||||||
438 | |||||||
439 | /** |
||||||
440 | * Create a MetaBundle in the db for each site, from the passed in $sourceModel |
||||||
441 | * |
||||||
442 | * @param Model $sourceModel |
||||||
443 | */ |
||||||
444 | public static function createContentMetaBundle(Model $sourceModel) |
||||||
445 | { |
||||||
446 | /** @var CampaignTypeModel $sourceModel */ |
||||||
447 | $sourceModel->attachBehavior('SEOmaticCampaignBehavior', CampaignBehavior::class); |
||||||
448 | $sites = Craft::$app->getSites()->getAllSites(); |
||||||
449 | /** @var Site $site */ |
||||||
450 | foreach ($sites as $site) { |
||||||
451 | $seoElement = self::class; |
||||||
452 | Seomatic::$plugin->metaBundles->createMetaBundleFromSeoElement($seoElement, $sourceModel, $site->id, null, true); |
||||||
453 | } |
||||||
454 | } |
||||||
455 | |||||||
456 | /** |
||||||
457 | * Create all the MetaBundles in the db for this Seo Element |
||||||
458 | */ |
||||||
459 | public static function createAllContentMetaBundles() |
||||||
460 | { |
||||||
461 | // Get all of the campaign types with URLs |
||||||
462 | $campaignTypes = Campaign::$plugin->campaignTypes->getAllCampaignTypes(); |
||||||
463 | foreach ($campaignTypes as $campaignType) { |
||||||
464 | self::createContentMetaBundle($campaignType); |
||||||
465 | } |
||||||
466 | } |
||||||
467 | } |
||||||
468 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths