Passed
Push — develop-v4 ( 2d4983...bb740f )
by Andrew
23:04
created

SeoShopifyProduct::sourceModelFromId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS 3.x
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) 2020 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\seoelements;
13
14
use Craft;
15
use craft\base\ElementInterface;
0 ignored issues
show
Bug introduced by
The type craft\base\ElementInterface was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use craft\base\Model;
17
use craft\elements\db\ElementQueryInterface;
18
use craft\events\DefineHtmlEvent;
19
use craft\models\Site;
20
use craft\shopify\elements\Product;
0 ignored issues
show
Bug introduced by
The type craft\shopify\elements\Product was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use craft\shopify\events\ShopifyProductSyncEvent;
0 ignored issues
show
Bug introduced by
The type craft\shopify\events\ShopifyProductSyncEvent was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use craft\shopify\services\Products;
0 ignored issues
show
Bug introduced by
The type craft\shopify\services\Products was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset;
24
use nystudio107\seomatic\base\SeoElementInterface;
25
use nystudio107\seomatic\helpers\ArrayHelper;
26
use nystudio107\seomatic\helpers\Config as ConfigHelper;
27
use nystudio107\seomatic\helpers\PluginTemplate;
28
use nystudio107\seomatic\integrations\shopify\ProductType;
29
use nystudio107\seomatic\models\MetaBundle;
30
use nystudio107\seomatic\Seomatic;
31
use yii\base\Event;
32
33
/**
34
 * @author    nystudio107
35
 * @package   Seomatic
36
 * @since     3.3.4
37
 */
38
class SeoShopifyProduct implements SeoElementInterface
39
{
40
    // Constants
41
    // =========================================================================
42
43
    const META_BUNDLE_TYPE = 'shopifyproduct';
44
    const ELEMENT_CLASSES = [
45
        Product::class,
46
    ];
47
    const REQUIRED_PLUGIN_HANDLE = 'shopify';
48
    const CONFIG_FILE_PATH = 'shopifyproductmeta/Bundle';
49
50
    // Public Static Properties
51
    // =========================================================================
52
53
    public static ?ProductType $productType = null;
54
55
    // Public Static Methods
56
    // =========================================================================
57
58
    /**
59
     * Return the sourceBundleType for that this SeoElement handles
60
     *
61
     * @return string
62
     */
63
    public static function getMetaBundleType(): string
64
    {
65
        return self::META_BUNDLE_TYPE;
66
    }
67
68
    /**
69
     * Returns an array of the element classes that are handled by this SeoElement
70
     *
71
     * @return array
72
     */
73
    public static function getElementClasses(): array
74
    {
75
        return self::ELEMENT_CLASSES;
76
    }
77
78
    /**
79
     * Return the refHandle (e.g.: `entry` or `category`) for the SeoElement
80
     *
81
     * @return string
82
     */
83
    public static function getElementRefHandle(): string
84
    {
85
        return 'product';
86
    }
87
88
    /**
89
     * Return the handle to a required plugin for this SeoElement type
90
     *
91
     * @return null|string
92
     */
93
    public static function getRequiredPluginHandle()
94
    {
95
        return self::REQUIRED_PLUGIN_HANDLE;
96
    }
97
98
    /**
99
     * Install any event handlers for this SeoElement type
100
     */
101
    public static function installEventHandlers()
102
    {
103
        $request = Craft::$app->getRequest();
104
105
        // Install for all requests
106
        Event::on(
107
            Products::class,
108
            Products::EVENT_BEFORE_SYNCHRONIZE_PRODUCT,
109
            function (ShopifyProductSyncEvent $event) {
0 ignored issues
show
Unused Code introduced by
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 ignore-unused  annotation

109
            function (/** @scrutinizer ignore-unused */ ShopifyProductSyncEvent $event) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
110
                Craft::debug(
111
                    'Products::EVENT_BEFORE_SYNCHRONIZE_PRODUCT',
112
                    __METHOD__
113
                );
114
                // Ideally we do something useful here, but since there is no concept of "sections" for
115
                // Shopify products, and our element save listeners will already take care of invalidating
116
                // the element changes, there's nothing to do here currently
117
            }
118
        );
119
120
        // Install for all non-console requests
121
        if (!$request->getIsConsoleRequest()) {
122
            // There is no concept of "sections" for shopify products, and so nothing additional to invalidate
123
        }
124
125
        // Install only for non-console site requests
126
        if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
127
        }
128
129
        // Handler: Product::EVENT_DEFINE_SIDEBAR_HTML
130
        Event::on(
131
            Product::class,
132
            Product::EVENT_DEFINE_SIDEBAR_HTML,
133
            static function (DefineHtmlEvent $event) {
134
                Craft::debug(
135
                    'Product::EVENT_DEFINE_SIDEBAR_HTML',
136
                    __METHOD__
137
                );
138
                $html = '';
139
                Seomatic::$view->registerAssetBundle(SeomaticAsset::class);
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

139
                Seomatic::$view->/** @scrutinizer ignore-call */ 
140
                                 registerAssetBundle(SeomaticAsset::class);

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.

Loading history...
140
                /** @var  $product Product */
141
                $product = $event->sender ?? null;
142
                if ($product !== null && $product->uri !== null) {
143
                    Seomatic::$plugin->metaContainers->previewMetaContainers($product->uri, $product->siteId, true);
144
                    // Render our preview sidebar template
145
                    if (Seomatic::$settings->displayPreviewSidebar) {
146
                        $html .= PluginTemplate::renderPluginTemplate('_sidebars/product-preview.twig');
147
                    }
148
                    // Render our analysis sidebar template
149
// @TODO: This will be added an upcoming 'pro' edition
150
//                if (Seomatic::$settings->displayAnalysisSidebar) {
151
//                    $html .= PluginTemplate::renderPluginTemplate('_sidebars/product-analysis.twig');
152
//                }
153
                }
154
                $event->html .= $html;
155
            }
156
        );
157
    }
158
159
    /**
160
     * Return an ElementQuery for the sitemap elements for the given MetaBundle
161
     *
162
     * @param MetaBundle $metaBundle
163
     *
164
     * @return ElementQueryInterface
165
     */
166
    public static function sitemapElementsQuery(MetaBundle $metaBundle): ElementQueryInterface
167
    {
168
        $query = Product::find()
169
            ->siteId($metaBundle->sourceSiteId)
170
            ->limit($metaBundle->metaSitemapVars->sitemapLimit);
171
172
        return $query;
173
    }
174
175
    /**
176
     * Return an ElementInterface for the sitemap alt element for the given MetaBundle
177
     * and Element ID
178
     *
179
     * @param MetaBundle $metaBundle
180
     * @param int $elementId
181
     * @param int $siteId
182
     *
183
     * @return null|ElementInterface
184
     */
185
    public static function sitemapAltElement(
186
        MetaBundle $metaBundle,
187
        int        $elementId,
188
        int        $siteId
189
    )
190
    {
191
        return Product::find()
192
            ->id($elementId)
193
            ->siteId($siteId)
194
            ->limit(1)
195
            ->one();
196
    }
197
198
    /**
199
     * Return a preview URI for a given $sourceHandle and $siteId
200
     * This just returns the first element
201
     *
202
     * @param string $sourceHandle
203
     * @param int|null $siteId
204
     *
205
     * @return string|null
206
     */
207
    public static function previewUri(string $sourceHandle, $siteId)
208
    {
209
        $uri = null;
210
        $element = Product::find()
211
            ->siteId($siteId)
212
            ->one();
213
        if ($element) {
214
            $uri = $element->uri;
215
        }
216
217
        return $uri;
218
    }
219
220
    /**
221
     * Return an array of FieldLayouts from the $sourceHandle
222
     *
223
     * @param string $sourceHandle
224
     *
225
     * @return array
226
     */
227
    public static function fieldLayouts(string $sourceHandle): array
228
    {
229
        $layouts = [];
230
        $layouts[] = self::getProductTypeModel()->getProductFieldLayout();
231
232
        return $layouts;
233
    }
234
235
    /**
236
     * Return the (entry) type menu as a $id => $name associative array
237
     *
238
     * @param string $sourceHandle
239
     *
240
     * @return array
241
     */
242
    public static function typeMenuFromHandle(string $sourceHandle): array
243
    {
244
        return [];
245
    }
246
247
    /**
248
     * Return the source model of the given $sourceId
249
     *
250
     * @param int $sourceId
251
     *
252
     * @return null
253
     */
254
    public static function sourceModelFromId(int $sourceId)
255
    {
256
        return self::getProductTypeModel();
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getProductTypeModel() returns the type nystudio107\seomatic\int...ons\shopify\ProductType which is incompatible with the documented return type null.
Loading history...
257
    }
258
259
    /**
260
     * Return the source model of the given $sourceHandle
261
     *
262
     * @param string $sourceHandle
263
     *
264
     * @return null
265
     */
266
    public static function sourceModelFromHandle(string $sourceHandle)
267
    {
268
        return self::getProductTypeModel();
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getProductTypeModel() returns the type nystudio107\seomatic\int...ons\shopify\ProductType which is incompatible with the documented return type null.
Loading history...
269
    }
270
271
    /**
272
     * Return the most recently updated Element from a given source model
273
     *
274
     * @param ?Model $sourceModel
275
     * @param int $sourceSiteId
276
     *
277
     * @return null|ElementInterface
278
     */
279
    public static function mostRecentElement(?Model $sourceModel, int $sourceSiteId)
280
    {
281
        return Product::find()
282
            ->siteId($sourceSiteId)
283
            ->limit(1)
284
            ->orderBy(['elements.dateUpdated' => SORT_DESC])
285
            ->one();
286
    }
287
288
    /**
289
     * Return the path to the config file directory
290
     *
291
     * @return string
292
     */
293
    public static function configFilePath(): string
294
    {
295
        return self::CONFIG_FILE_PATH;
296
    }
297
298
    /**
299
     * Return a meta bundle config array for the given $sourceModel
300
     *
301
     * @param ?Model $sourceModel
302
     *
303
     * @return array
304
     */
305
    public static function metaBundleConfig(?Model $sourceModel): array
306
    {
307
        return ArrayHelper::merge(
308
            ConfigHelper::getConfigFromFile(self::configFilePath()),
309
            [
310
                'sourceId' => self::getProductTypeModel()->id,
311
                'sourceName' => self::getProductTypeModel()->name,
312
                'sourceHandle' => self::getProductTypeModel()->handle,
313
            ]
314
        );
315
    }
316
317
    /**
318
     * Return the source id from the $element
319
     *
320
     * @param ElementInterface $element
321
     *
322
     * @return int|null
323
     */
324
    public static function sourceIdFromElement(ElementInterface $element)
325
    {
326
        return self::getProductTypeModel()->id;
327
    }
328
329
    /**
330
     * Return the (product) type id from the $element
331
     *
332
     * @param ElementInterface $element
333
     *
334
     * @return int|null
335
     */
336
    public static function typeIdFromElement(ElementInterface $element)
337
    {
338
        return null;
339
    }
340
341
    /**
342
     * Return the source handle from the $element
343
     *
344
     * @param ElementInterface $element
345
     *
346
     * @return string|null
347
     */
348
    public static function sourceHandleFromElement(ElementInterface $element)
349
    {
350
        return self::getProductTypeModel()->handle;
351
    }
352
353
    /**
354
     * Create a MetaBundle in the db for each site, from the passed in $sourceModel
355
     *
356
     * @param ?Model $sourceModel
357
     */
358
    public static function createContentMetaBundle(?Model $sourceModel)
359
    {
360
        /** @var ProductType $sourceModel */
361
        $sites = Craft::$app->getSites()->getAllSites();
362
        /** @var Site $site */
363
        foreach ($sites as $site) {
364
            $seoElement = self::class;
365
            /** @var SeoElementInterface $seoElement */
366
            Seomatic::$plugin->metaBundles->createMetaBundleFromSeoElement($seoElement, $sourceModel, $site->id);
367
        }
368
    }
369
370
    /**
371
     * Create all the MetaBundles in the db for this Seo Element
372
     */
373
    public static function createAllContentMetaBundles()
374
    {
375
        self::createContentMetaBundle(self::getProductTypeModel());
376
    }
377
378
    // Protected Static Methods
379
    // =========================================================================
380
381
    /**
382
     * Return a memoized singleton of our faux ProductType
383
     *
384
     * @return ProductType
385
     */
386
    protected static function getProductTypeModel(): ProductType
387
    {
388
        if (!self::$productType) {
389
            self::$productType = new ProductType();
390
        }
391
392
        return self::$productType;
393
    }
394
}
395