studioespresso/craft-seo-fields

View on GitHub
src/services/RedirectService.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

namespace studioespresso\seofields\services;

use Craft;
use craft\base\Component;
use craft\base\Element;
use craft\helpers\App;
use craft\helpers\DateTimeHelper;
use craft\helpers\ElementHelper;
use craft\helpers\Json;
use craft\helpers\UrlHelper;
use studioespresso\seofields\models\RedirectModel;
use studioespresso\seofields\records\RedirectRecord;
use studioespresso\seofields\SeoFields;
use yii\base\ExitException;

/**
 * @author    Studio Espresso
 * @package   SeoFields
 * @since     1.0.0
 */
class RedirectService extends Component
{
    public $oldUris = [];


    public function trackElementUris(Element $element)
    {
        if (empty($this->oldUris[$element->id])) {
            $this->oldUris[$element->id] = $this->getElementUrls($element);
        }
    }

    public function handleUriChange(Element $element)
    {
        if (empty($this->oldUris[$element->id])) {
            return;
        }

        foreach ($this->oldUris[$element->id] as $siteId => $oldUri) {
            $newUri = Craft::$app->getElements()->getElementUriForSite($element->id, $siteId);
            // Should be keep trailing slashes into account here (or when the old one have those as well)
            if (Craft::$app->config->general->addTrailingSlashesToUrls) {
                $oldUri = rtrim($oldUri, '/') . '/';
                $newUri = rtrim($newUri, '/') . '/';
            }

            if ($newUri !== $oldUri) {
                // Let's add a redirect
                $oldUrl = UrlHelper::siteUrl($oldUri, null, null, $siteId);
                $newUrl = UrlHelper::siteUrl($newUri, null, null, $siteId);
                $redirect = new RedirectModel();
                $redirect->pattern = $oldUri;
                $redirect->sourceMatch = 'path';
                $redirect->redirect = $newUrl;
                $redirect->matchType = 'exact';
                $redirect->siteId = $siteId;
                $redirect->method = 301;
                $this->saveRedirect($redirect);
            }
        }
    }


    public function handleRedirect(RedirectRecord|array $redirect)
    {
        if (is_array($redirect)) {
            $record = $redirect['record'];
            $model = new RedirectModel($record->getAttributes());
        } else {
            $model = new RedirectModel($redirect->getAttributes());
        }
        Craft::debug("Found a redirect for this 404, redirecting", SeoFields::class);
        $this->updateOnRedirect($model);
        $this->redirect($redirect);

        try {
            Craft::$app->end();
        } catch (ExitException $e) {
            Craft::error($e->getMessage(), __METHOD__);
        }
    }

    public function getRedirectById($id)
    {
        $record = RedirectRecord::findOne(['id' => $id]);
        $model = new RedirectModel();
        $model->setAttributes($record->getAttributes());
        return $model;
    }

    public function getAllRedirects($searchParam = null)
    {
        $query = RedirectRecord::find();
        if ($searchParam) {
            $query->where(['like', 'pattern', $searchParam]);
            $query->orWhere(['like', 'redirect', $searchParam]);
        }
        return $query->all();
    }

    private function updateOnRedirect(RedirectModel $model)
    {
        $model->counter++;
        $model->dateLastHit = DateTimeHelper::toIso8601(time());

        $model->validate();
        $this->saveRedirect($model);
    }

    public function saveRedirect(RedirectModel $model)
    {
        $record = false;
        if ($model->id) {
            $record = RedirectRecord::findOne(['id' => $model->id]);
        } else {
            $record = new RedirectRecord();
        }

        $record->setAttribute('siteId', $model->siteId === "0" ? null : $model->siteId);
        if (substr($model->pattern, 0, 1) == '/') {
            $record->setAttribute('pattern', $model->pattern);
        } else {
            $record->setAttribute('pattern', "/$model->pattern");
        }
        $record->setAttribute('sourceMatch', $model->sourceMatch);
        $record->setAttribute('redirect', $model->redirect);
        $record->setAttribute('matchType', $model->matchType);
        $record->setAttribute('counter', $model->counter);
        $record->setAttribute('dateLastHit', $model->dateLastHit);
        $record->setAttribute('method', $model->method);

        if ($record->save()) {
            return true;
        }
    }

    public function deleteRedirectById($id)
    {
        $record = RedirectRecord::findOne(['id' => $id]);
        if ($record->delete()) {
            return true;
        }
    }

    public function import($data, $settings)
    {
        App::maxPowerCaptain();
        $patternCol = $settings['patternCol'];
        $redirectCol = $settings['redirectCol'];
        $validRedirects = [];
        $invalidRedirects = [];

        foreach ($data as $row) {
            $row = array_values($row);
            $pattern = $row[$patternCol];
            $redirect = $row[$redirectCol];
            if ($pattern === $redirect) {
                continue;
            } elseif (substr($redirect, 0, 1) != "/") {
                $invalidRedirects[] = $row;
                continue;
            }
            $validRedirects[] = $row;
        }

        foreach ($validRedirects as $row) {
            $model = new RedirectModel();
            $model->pattern = $row[$patternCol];
            $model->redirect = $row[$redirectCol];
            $model->matchType = 'exact';
            $model->sourceMatch = 'path';
            $model->siteId = $settings['siteId'];
            $model->method = $settings['method'];
            if (!$model->validate()) {
                Craft::error(Json::encode($model->getErrors()));
            }
            $this->saveRedirect($model);
        }

        return [
            'imported' => $validRedirects,
            'invalid' => $invalidRedirects,
        ];
    }

    private function redirect(RedirectModel|RedirectRecord|array $redirect)
    {
        try {
            if (is_array($redirect)) {
                $url = $redirect['url'];
                $method = $redirect['record']->method;
            } else {
                $method = $redirect->method;
                if ($redirect->siteId) {
                    $url = UrlHelper::siteUrl($redirect->redirect, null, null, $redirect->siteId);
                } else {
                    $url = $redirect->redirect;
                }
            }

            $response = Craft::$app->response;
            if (Craft::$app->getRequest()->getQueryStringWithoutPath()) {
                $response->redirect($url . "?" . Craft::$app->getRequest()->getQueryStringWithoutPath(), $method)->send();
            }

            $response->redirect($url, $method)->send();
        } catch (\Exception $e) {
        }
        return;
    }

    private function getElementUrls(Element $element)
    {
        $uris = [];
        if (!ElementHelper::isDraftOrRevision($element) && $element->id) {
            foreach (Craft::$app->getSites()->getAllSites(true) as $site) {
                $uri = Craft::$app->getElements()->getElementUriForSite($element->id, $site->id);
                if ($uri) {
                    $uris[$site->id] = $uri;
                }
            }
        }

        return $uris;
    }
}