Verwendung von UrlManager zum Konfigurieren des Routings und zum Erstellen von benutzerfreundlichen URLs


    Hallo liebe Leser! Ich setze eine Reihe von Artikeln darüber fort, wie wir ein atypisches, großes Projekt mit dem Yii2-Framework und AngularJS entwickelt haben.

    In einem früheren Artikel habe ich die Vorteile unseres gewählten Technologie-Stacks beschrieben und eine modulare Architektur für unsere Anwendung vorgeschlagen .

    In diesem Artikel wird das Konfigurieren des Routings und das Erstellen von URLs mit urlManager für jedes Modul einzeln behandelt. Ich werde auch den Prozess des Erstellens eigener Regeln für bestimmte URLs sortieren, indem ich eine Klasse schreibe, die UrlRuleInterface erweitert. Abschließend beschreibe ich, wie wir die Erzeugung und Ausgabe von Metatags für öffentliche Seiten der Site implementiert haben.

    Das interessanteste unter dem Schnitt.

    URL-Regeln


    Ich gehe davon aus, dass Sie UrlManager höchstwahrscheinlich schon früher verwendet haben, zumindest um die CNC zu aktivieren und index.php vor der URL zu verbergen.

    //...
    'urlManager' => [
        'class' => 'yii\web\UrlManager',
        'enablePrettyUrl' => true,
        'showScriptName' => false,
    ],
    /..
    

    Aber UrlManager kann noch viel mehr. Schöne URLs haben einen großen Einfluss auf die Website-Bereitstellung in Suchmaschinen. Es ist auch zu berücksichtigen, dass Sie die Struktur Ihrer Anwendung ausblenden können, indem Sie Ihre eigenen Regeln definieren.

    'enableStrictParsing' => true ist eine sehr nützliche Eigenschaft, die den Zugriff nur auf bereits konfigurierte Regeln einschränkt. Im Konfigurationsbeispiel zeigt die Route www.our-site.com auf site / default / index, aber www.our-site.com/site/default/index zeigt Seite 404 an.

    //...
    'urlManager' => [
        'class' => 'yii\web\UrlManager',
        'enablePrettyUrl' => true,
        'showScriptName' => false,
        'enableStrictParsing' => true,
        'rules' => [
            '/' => 'site/default/index',
        ],
    ],
    /..
    

    Da wir die Anwendung in Module unterteilt haben und diese Module so unabhängig wie möglich voneinander sein sollen, können URL-Regeln dynamisch zum URL-Manager hinzugefügt werden. Auf diese Weise können die Module verteilt und wiederverwendet werden, ohne dass UrlManager konfiguriert werden muss, da die Module ihre eigenen URL-Regeln verwalten.

    Damit dynamisch hinzugefügte Regeln während des Routing-Vorgangs wirksam werden, müssen Sie sie in der Selbstoptimierungsphase hinzufügen. Für Module bedeutet dies, dass sie yii \ base \ BootstrapInterface implementieren und der Bootmethode bootstrap () folgende Regeln hinzufügen müssen:

    getUrlManager()->addRules(
                [
                    // объявление правил здесь
                    '' => 'site/default/index',
                    '<_a:(about|contacts)>' => 'site/default/<_a>'
                ]
            );
        }
    }
    

    Wir fügen die Datei Bootstrap.php mit diesem Code zum Modulordner / modules / site / hinzu. Und wir werden eine solche Datei in jedem Modul haben, die ihre eigenen URL-Regeln hinzufügt.

    Beachten Sie, dass Sie diese Module auch in yii \ web \ Application :: bootstrap () auflisten müssen, damit sie am Selbstoptimierungsprozess teilnehmen können. Listen Sie dazu die Module im Bootstrap-Array in der Datei /frontend/config/main.php auf:

    //...
        'params' => require(__DIR__ . '/params.php'),
        'bootstrap' => [
            'modules\site\Bootstrap',
            'modules\users\Bootstrap',
            'modules\cars\Bootstrap'
            'modules\lease\Bootstrap'
            'modules\seo\Bootstrap'
            ],
    ];
    

    Bitte beachten Sie, dass ich seit dem Schreiben des ersten Artikels einige weitere Module hinzugefügt habe:

    • Module / Benutzer - Das Modul, in dem Vorgänge mit dem Benutzer und allen Seiten des Benutzers verarbeitet werden (Registrierung, Login, persönlicher Account)
    • Module / Autos - Ein Modul, in dem sie mit einer Datenbank von Marken, Marken und Modifikationen von Autos arbeiten.
    • modules / lease - Das Modul, in dem von Nutzern hinzugefügte Anzeigen verarbeitet werden.
    • modules / seo - Modul für SEO. Alle Komponenten und Helfer werden hier gespeichert, damit wir die SEO-Anforderungen erfüllen können. Ich werde weiter darüber schreiben.

    Benutzerdefinierte URL-Regeln


    Obwohl die Standardklasse yii \ web \ UrlRule für die meisten Projekte flexibel genug ist, gibt es Situationen, in denen Sie Ihre eigenen Regelklassen erstellen müssen.

    Zum Beispiel können Sie auf der Website eines Autohändlers ein URL-Format wie / new-lease / state / Fabrikat-Modell-Ort / Jahr unterstützen, wobei Staat, Fabrikat, Modell, Jahr und Ort einigen in der Basistabelle gespeicherten Daten entsprechen müssen Daten. Die Standardklasse funktioniert hier nicht, da sie auf statisch deklarierten Mustern basiert.

    Lassen Sie uns ein wenig vom Code abschweifen, und ich werde Ihnen die Essenz der Aufgabe beschreiben, mit der wir konfrontiert waren.

    Entsprechend der Spezifikation mussten die folgenden Seitentypen mit den entsprechenden Regeln zum Generieren von URL- und Metatags erstellt werden:

    Suchergebnisseiten


    Sie sind wiederum in drei Typen unterteilt:

    Anzeigen von Händlern
    URL: / new-lease / (Bundesland) / (Marke) - (Modell) - (Standort)
    URL: / new-lease / (Bundesland) / (Marke) - (Modell) - (Standort) / (Jahr)

    Benutzerdefinierte Anzeigen:
    URL: / Leasingübertragung / (Bundesland) / (Marke) - (Modell) - (Standort)
    URL: / Leasingübertragung / (Bundesland) / (Marke) - (Modell) - (Standort) / (Jahr)

    Zum Beispiel: / new-lease / NY / volkswagen-GTI-New-York-City / 2015

    Suchergebnisse, wenn der Ort nicht im Filter angegeben ist:
    / (new-lease | lease-transfer) / (Marke) - (Modell) / ( Jahr)

    Titel: (Marke) (Modell) (Jahr) zur Anmietung in (Ort). (New Leases | Lease Transfers)
    Zum Beispiel: Volkswagen GTI 2015 für Lease in New York City. Dealer Leases.
    Schlüsselwörter: (Marke), (Modell), (Jahr), für, Leasing, in, (Ort), (Neu, Leasing | Leasing, Übertragungen)
    Beschreibung: Liste von (Marke) (Modell) (Jahr) in (Ort) zur Miete verfügbar. (Dealer Leases | Lease Transfers).

    Anzeigenprüfungsseite


    Sie sind wiederum in zwei Typen unterteilt:

    Händlerankündigung:
    URL: / new-lease / (Bundesland) / (Marke) - (Modell) - (Jahr) - (Farbe) - (Kraftstoffart) - (Standort) - (ID)

    Benutzerdefinierte Erklärung:
    URL: / Leasingübertragung / (Bundesland) / (Marke) - (Modell) - (Jahr) - (Farbe) - (Kraftstofftyp) - (Standort) - (ID)

    Anrede: (Marke) - (Modell) - (Jahr) - (Farbe) - (Kraftstoffart) zur Anmietung in (Ort)
    Stichworte: (Jahr), (Marke), (Modell), (Farbe), (Kraftstoffart) , (Ort), für, Mietvertrag
    Beschreibung: (Jahr) (Marke) (Modell) (Farbe) (Kraftstoffart) für Mietvertrag (Ort)

    Fahrzeuginformationsseiten


    URL: / i / (Marke) - (Modell) - (Jahr)
    Titel: (Marke) - (Modell) - (Jahr)
    Schlüsselwörter: (Jahr), (Marke), (Modell)
    Beschreibung: (Jahr), (Marke) ), (Modell)

    Dies ist nur ein Teil der Seiten, die wir implementieren mussten. Hier finden Sie einen Auszug aus der Leistungsbeschreibung, damit Sie verstehen, was wir vom URL-Manager erreichen müssen und wie untrivial die Regeln sind.

    Mit Yii2 können Sie benutzerdefinierte URLs durch Analyse und URL-Generierungslogik definieren, indem Sie eine benutzerdefinierte UrlRule-Klasse erstellen. Wenn Sie Ihre eigene UrlRule erstellen möchten, können Sie entweder den Code aus yii \ web \ UrlRule kopieren und erweitern oder in einigen Fällen einfach yii \ web \ UrlRuleInterface implementieren.

    Nachfolgend finden Sie den Code, den unser Team für die von uns diskutierte URL-Struktur geschrieben hat. Für ihn haben wir die Datei /modules/seo/components/UrlRule.php erstellt. Ich betrachte diesen Code nicht als Standard, aber ich bin sicher, dass er die Aufgabe eindeutig erfüllt.

    url = str_replace(' ', '_', substr($params['url'],1) );
                        $route->route = 'lease/search/index';
                        $route->params = json_encode(['make'=>$params['make'], 'model'=>$params['model'], 'location'=>$params['location']  ]);
                        $route->save();
                        return '/'.$params['url'];
                    }
                }
                if (isset($params['type']) && in_array($params['type'], ['user','dealer'])) {
                    $type = ($params['type'] == 'dealer')? 'new-lease' : 'lease-transfer';
                } else {
                    return false;
                }
                if ((isset($params['zip']) && !empty($params['zip'])) || (isset($params['location']) && isset($params['state']))) {
                    // make model price zip type
                    if (isset($params['zip']) && !empty($params['zip'])) {
                        $zipdata = Zip::findOneByZip($params['zip']);
                    } else {
                        $zipdata = Zip::findOneByLocation($params['location'], $params['state']);
                    }
                    // city state_code
                    if (!empty($zipdata)) {
                        $url = $type . '/' . $zipdata['state_code'] . '/' . $params['make'] . '-' . $params['model'] . '-' . $zipdata['city'];
                        if (!empty($params['year'])) {
                            $url.='/'.$params['year'];
                        }
                        $url = str_replace(' ', '_', $url);
                        if($search_url = Route::findRouteByUrl($url)) {
                            return '/'.$url;
                        } else {
                            $route = new Route();
                            $route->url = str_replace(' ','_',$url);
                            $route->route = 'lease/search/index';
                            $pars = ['make'=>$params['make'], 'model'=>$params['model'], 'location'=>$zipdata['city'], 'state'=>$zipdata['state_code'] ]; //, 'zip'=>$params['zip'] ];
                            if (!empty($params['year'])) {
                                $pars['year']=$params['year'];
                            }
                            $route->params = json_encode($pars);
                            $route->save();
                            return $route->url;
                        }
                    }
                }
                if (isset($params['make'], $params['model'] )) {
                    $url = $type . '/' . $params['make'] . '-' . $params['model'] ;
                    if (!empty($params['year'])) {
                        $url.='/'.$params['year'];
                    }
                    $url = str_replace(' ', '_', $url);
                    if($search_url = Route::findRouteByUrl($url)) {
                        return '/'.$url;
                    } else {
                        $route = new Route();
                        $route->url = str_replace(' ','_',$url);
                        $route->route = 'lease/search/index';
                        $pars = ['make'=>$params['make'], 'model'=>$params['model']  ];
                        if (!empty($params['year'])) {
                            $pars['year']=$params['year'];
                        }
                        $route->params = json_encode($pars);
                        $route->save();
                        return $route->url;
                    }
                }
            }
            return false;
        }
        /**
         * Parse request
         * @param \yii\web\Request|UrlManager $manager
         * @param \yii\web\Request $request
         * @return array|boolean
         */
        public function parseRequest($manager, $request)
        {
            $pathInfo = $request->getPathInfo();
            /**
             * Parse request for search URLs with location and year
             */
            if (preg_match('%^(?Please-transfer|new-lease)\/(?P[A-Za-z]{2})\/(?P[._\sA-Za-z-0-9-]+)\/(?P\d{4})?%', $pathInfo, $matches)) {
                $route = Route::findRouteByUrl($pathInfo);
                if (!$route) {
                    return false;
                }
                $params = [
                    'node' => $matches['url'] . '/' . $matches['year'],
                    'role' => $matches['role'],
                    'state' => $matches['state'],
                    'year' => $matches['year']
                ];
                if (!empty($route['params'])) {
                    $params = array_merge($params, json_decode($route['params'], true));
                }
                return [$route['route'], $params];
            }
            /**
             * Parse request for search URLs with location and with year
             */
            if (preg_match('%^(?Please-transfer|new-lease)\/(?P[._\sA-Za-z-0-9-]+)\/(?P\d{4})%', $pathInfo, $matches)) {
                $route = Route::findRouteByUrl($pathInfo);
                if (!$route) {
                    return false;
                }
                $params = [
                    'node' => $matches['url'] . '/' . $matches['year'],
                    'role' => $matches['role'],
                    'year' => $matches['year']
                ];
                if (!empty($route['params'])) {
                    $params = array_merge($params, json_decode($route['params'], true));
                }
                return [$route['route'], $params];
            }
            /**
             * Parse request for leases URLs and search URLs with location
             */
            if (preg_match('%^(?Please-transfer|new-lease)\/(?P[A-Za-z]{2})\/(?P[_A-Za-z-0-9-]+)?%', $pathInfo, $matches)) {
                $route = Route::findRouteByUrl([$matches['url'], $pathInfo]);
                if (!$route) {
                    return false;
                }
                $params = [
                    'role' => $matches['role'],
                    'node' => $matches['url'],
                    'state' => $matches['state']
                ];
                if (!empty($route['params'])) {
                    $params = array_merge($params, json_decode($route['params'], true));
                }
                return [$route['route'], $params];
            }
            /**
             * Parse request for search URLs without location and year
             */
            if (preg_match('%^(?Please-transfer|new-lease)\/(?P[._\sA-Za-z-0-9-]+)?%', $pathInfo, $matches)) {
                $route = Route::findRouteByUrl($pathInfo);
                if (!$route) {
                    return false;
                }
                $params = [
                    'node' => $matches['url'],
                    'role' => $matches['role'],
                ];
                if (!empty($route['params'])) {
                    $params = array_merge($params, json_decode($route['params'], true));
                }
                return [$route['route'], $params];
            }
            /**
             * Parse request for Information pages URLs
             */
            if (preg_match('%^i\/(?P[_A-Za-z-0-9-]+)?%', $pathInfo, $matches)) {
                $route = Route::findRouteByUrl($matches['url']);
                if (!$route) {
                    return false;
                }
                $params = Json::decode($route['params']);
                $params['node'] = $route['url'];
                return [$route['route'], $params];
            }
            return false;
        }
    }
    

    Um es zu verwenden, müssen Sie nur diese Klasse zum Array von Regeln yii \ web \ UrlManager :: $ rules hinzufügen.

    Dazu erstellen Sie die Datei Bootstrap.php im Modul / modules / seo (ähnlich der Datei Bootstrap.php im Modul / modules / site) und deklarieren darin die folgende Regel:
    //...
        public function bootstrap($app)
        {
            $app->getUrlManager()->addRules(
                [
                    [
                      'class' => 'modules\seo\components\UrlRule,
                    ],
                ]
            );
        }
    /..
    

    Diese Sonderregel gilt für einen ganz bestimmten Anwendungsfall. Wir haben nicht vor, diese Regel in anderen Projekten wiederzuverwenden, daher gibt es keine Einstellungen.

    Da die Regel im Allgemeinen nicht konfigurierbar ist, ist es nicht erforderlich, sie von yii \ web \ UrlRule, yii \ base \ Object oder von einer anderen Stelle aus zu erweitern. Es reicht aus, nur die Schnittstelle yii \ web \ UrlRuleInterface zu implementieren. Da wir nicht vorhaben, diese Regel in unseren wiederverwendeten Modulen wiederzuverwenden, haben wir sie im SEO-Modul definiert.

    parseRequest () betrachtet die Route und analysiert die Parameter weiter, wenn sie mit dem regulären Ausdruck in der Bedingung übereinstimmt.

    Bei dieser Methode verwenden wir das Hilfsroutenmodell, bei dem generierte Links im URL-Feld gespeichert werden. Sie suchen nach Konformität mit der findRouteByUrl-Methode. Diese Methode liefert uns einen Datensatz aus der Tabelle (falls vorhanden) mit den Feldern:

    • url - Teil der Suchabfrage, mit der der Datensatz gefunden wurde,
    • route - die Route, auf die die Kontrolle übertragen werden soll,
    • params - zusätzliche Parameter im JSON-Format, die für die weitere Arbeit übergeben werden müssen.

    parseRequest () gibt ein Array mit der Aktion und den Parametern zurück:

    [
                ‘lease/search/view’,
                [
                    'node' => new-lease/NY/ volkswagen-GTI-New-York-City/2016,
                    'role' => ‘new-lease’,
                    'state' => ‘NY’,
                        'year' => ‘2016’
                ]
    ]
    

    Andernfalls wird false zurückgegeben, um UrlManager anzuzeigen, dass die Anforderung nicht analysiert werden kann.

    createUrl () erstellt die URL aus den angegebenen Parametern, jedoch nur, wenn die URL für die Aktionen Leasing / Leasing / Ansicht, Autos / Info / Ansicht oder Leasing / Suche / Ansicht vorgeschlagen wurde.

    Aus Performancegründen


    Bei der Entwicklung komplexer Webanwendungen ist es wichtig, URL-Regeln zu optimieren, damit das Parsen von Anforderungen und das Erstellen von URLs weniger Zeit in Anspruch nimmt.

    Beim Parsen oder Erstellen einer URL analysiert der URL-Manager die URL-Regeln in der Reihenfolge, in der sie deklariert wurden. Daher können Sie die Reihenfolge der URL-Regeln anpassen, sodass spezifischere und / oder häufig verwendete Regeln vor den weniger verwendeten Regeln stehen.

    Es kommt häufig vor, dass Ihre Anwendung aus Modulen besteht, von denen jedes einen eigenen Satz von URL-Regeln mit Modul-ID sowie ein gemeinsames Präfix hat.

    Erzeugung und Ausgabe von Meta-Tags


    Um Meta-Tags in einem bestimmten Format für die angegebenen Seitentypen zu generieren und anzuzeigen, wurde ein spezieller Helfer geschrieben, der sich in der Datei modules / seo / helpers / Meta.php befindet. Es enthält den folgenden Code:

    view->registerMetaTag(['name' => 'keywords','content' => 'lease, car, transfer']);
                    Yii::$app->view->registerMetaTag(['name' => 'description','content' => 'Carvoy - Change the way you lease! Lease your next new car online and we\'ll deliver it to your doorstep.']);
                    break;
                case 'lease':
                    $title = $model->make . ' - ' . $model->model . ' - ' . $model->year . ' - ' . $model->exterior_color . ' - ' . $model->engineFuelType . ' for lease in ' . $model->location;
                    Yii::$app->view->registerMetaTag(['name' => 'keywords','content' => Html::encode($model->year . ', ' . $model->make . ', ' . $model->model . ', ' . $model->exterior_color . ', ' . $model->engineFuelType . ', ' . $model->location . ', for, lease')]);
                    Yii::$app->view->registerMetaTag(['name' => 'description','content' => Html::encode($model->year . ' ' . $model->make . ' ' . $model->model . ' ' . $model->exterior_color . ' ' . $model->engineFuelType . ' for lease in ' . $model->location)]);
                    break;
                case 'info_page':
                    $title = $model->make . ' - ' . $model->model . ' - ' . $model->year;
                    Yii::$app->view->registerMetaTag(['name' => 'keywords','content' => Html::encode($model->year . ', ' . $model->make . ', ' . $model->model)]);
                    Yii::$app->view->registerMetaTag(['name' => 'description','content' => Html::encode($model->year . ' ' . $model->make . ' ' . $model->model)]);
                    break;
                case 'search':
                    if ($model['role'] == 'd') $role = 'Dealer Lease';
                    elseif ($model['role'] == 'u') $role = 'Lease Transfers';
                    else $role = 'All Leases';
                    if (isset($model['make']) && isset($model['model'])) {
                        $_make = (is_array($model['make']))? (( isset($model['make']) && ( count($model['make']) == 1) )? $model['make'][0] : false ) : $model['make'];
                        $_model = (is_array($model['model']))? (( isset($model['model']) && ( count($model['model']) == 1) )? $model['model'][0] : false ) : $model['model'];
                        $_year = false;
                        $_location = false;
                        if (isset($model['year'])) {
                            $_year = (is_array($model['year']))? (( isset($model['year']) && ( count($model['year']) == 1) )? $model['year'][0] : false ) : $model['year'];
                        }
                        if (isset($model['location'])) {
                            $_location = (is_array($model['location']))? (( isset($model['location']) && ( count($model['location']) == 1) )? $model['location'][0] : false ) : $model['location'];
                        }
                        if ( ($_make || $_model) && !(isset($model['make']) && ( count($model['make']) > 1)) ) {
                            $title = $_make . (($_model)? ' ' . $_model : '') . (($_year)? ' ' . $_year : '') . ' for Lease' . (($_location)? ' in ' . $_location . '. ' : '. ') . $role . '.';
                        } else {
                            $title = 'Vehicle for Lease' . (($_location)? ' in ' . $_location . '. ' : '. ') . $role . '.';
                        }
                        Yii::$app->view->registerMetaTag(['name' => 'keywords','content' => Html::encode( ltrim($_make . (($_model)? ', ' . $_model : '') . (($_year)? ', ' . $_year : '') . ', for, Lease' . (($_location)? ', in, ' . $_location : '') . ', ' . implode(', ', (explode(' ', $role))), ', ') ) ]);
                        Yii::$app->view->registerMetaTag(['name' => 'description','content' => Html::encode( 'List of '. ((!$_model && !$_make)? 'Vehicles' : '') . $_make . (($_model)? ' ' . $_model : '') . (($_year)? ' ' . $_year : '') . (($_location)? ' in ' . $_location : '') . ' available for lease. ' . $role . '.' )]);
                    } else {
                        $title = 'Search results';
                    }
                    break;
            }
            return $title;
        }
    }
    

    Wir verwenden diesen Helfer in der Ansicht der Seite, für die Sie Meta-Tags festlegen müssen. Fügen Sie beispielsweise für die Anzeigenprüfungsseite die folgende Zeile zur Datei /modules/lease/views/frontend/lease/view.php hinzu

    //...
        $this->title = \modules\seo\helpers\Meta::all('lease', $model);
    /..
    

    Der erste Parameter der Methode ist der Seitentyp, für den Metatags generiert werden. Der zweite Parameter übergibt das Modell der aktuellen Anzeige.

    Innerhalb der Methode werden abhängig vom Seitentyp Metatags generiert und mit der Methode registerMetaTag der Klasse yii \ web \ View zum Head hinzugefügt. Die Methode gibt den generierten String für das Title-Tag zurück. Daher legen wir über die Eigenschaft $ title der Klasse yii \ web \ View den Seitentitel fest.

    Vielen Dank für Ihre Aufmerksamkeit!

    Material vorbereitet von: greebn9k (Sergey Gribnyak), pavel-berezhnoy (Pavel Berezhnoy), silmarilion (Andrey Khakharev)

    Jetzt auch beliebt: