first working version

This commit is contained in:
Jérémy Munsch 2021-08-31 22:28:29 +02:00
commit f17a33abec
No known key found for this signature in database
GPG key ID: 677E1F9F80C16560
19 changed files with 9619 additions and 0 deletions

18
.editorconfig Normal file
View file

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.json]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
workspace.code-workspace
vendor

2
.styleci.yml Normal file
View file

@ -0,0 +1,2 @@
php:
preset: laravel

22
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,22 @@
{
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Pointez pour afficher la description des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9000
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9000
}
]
}

6
changelog.md Normal file
View file

@ -0,0 +1,6 @@
# Changelog
All notable changes to `refresh-sitemap` will be documented in this file.
## Version 0.0.1
First release version

45
composer.json Normal file
View file

@ -0,0 +1,45 @@
{
"name": "kwaadpepper/refresh-sitemap",
"description": "Create sitemap.xml for all routes",
"license": "MIT",
"authors": [
{
"name": "Jérémy Munsch",
"email": "github@jeremydev.ovh",
"homepage": "https://jeremydev.ovh"
}
],
"homepage": "https://github.com/kwaadpepper/refresh-sitemap",
"keywords": [
"Laravel",
"Sitemap"
],
"require": {
"illuminate/support": "~7|~8",
"laravel/framework": "~7|~8",
"spatie/laravel-sitemap": "^5.8",
"kwaadpepper/enum": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "~9.0",
"squizlabs/php_codesniffer": "^3.5",
"orchestra/testbench": "^6.13"
},
"autoload": {
"psr-4": {
"Kwaadpepper\\RefreshSitemap\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Kwaadpepper\\RefreshSitemap\\Tests\\": "tests"
}
},
"extra": {
"laravel": {
"providers": [
"Kwaadpepper\\RefreshSitemap\\RefreshSitemapServiceProvider"
]
}
}
}

8562
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,82 @@
<?php
use Spatie\Sitemap\Tags\Url;
return [
/**
* A list of url to ignore
* These are hard coded
* Each url does not have to be exhaustive
*
* For example, if you want to ignore all routes
* of the backoffice group
* just add '/backoffice' into this table
*/
'ignoreRoutes' => [],
/**
* This table is needed to resolve models matching
* routes
*
* it has to be filled like following
*
* 'products.meat-type' => [
* 'slugMeatType' => [MeatTypes::class, 'slug']
* ],
* 'kitchen-side.health-nutrition.balanced-menu.menu' => [
* 'type' => [BalancedMenuType::class, 'type']
* ]
*
* The index is the complete route name, the array content is an array
* within index is the argument name with its model class.
* This will allow the generator to generate a route for each of the models
* in this table
*/
'routesBinder' => [],
/**
* You can put route conditions here
* These will be used to decide whether the route should appear
* in the sitemap or not
*
* These are called in Eloquent where
*
* example
*
* 'queryConditions' => [
* 'status' => true,
* 'ocbStatus' => EleveurOcbStatus::yes()->value,
* 'star_moment' => true
* ]
*
*/
'queryConditions' => [],
/**
* The default route frequency to use with sitemap generator
*/
'routeDefaultFrequency' => Url::CHANGE_FREQUENCY_DAILY,
/**
* The default route priority to use with sitemap generator
*
* priority should be a number between 0 and 1
* https://www.sitemaps.org/protocol.html
*/
'routeDefaultPriority' => 0.5,
/**
* Frequencies for routes
* use the full or partial route name as index
* and a frequency within \Spatie\Sitemap\Tags\Url constants
*/
'routeFrequencies' => [],
/**
* Priorities for routes
* use the full or partial route name as index
* and a priority number between 0 and 1
*/
'routePriorities' => []
];

21
license.md Normal file
View file

@ -0,0 +1,21 @@
MIT License (!! includes CC BY-NC 3.0 parts please read Readme.md)
Copyright (c) 2021 Jérémy Munsch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

87
phpcs.xml Normal file
View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="RealWorld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<arg name="colors"/>
<arg name="extensions" value="php"/>
<file>src/</file>
<file>tests/</file>
<description>Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/
</description>
<rule ref="Generic.CodeAnalysis.EmptyStatement" />
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop" />
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall" />
<rule ref="Generic.CodeAnalysis.JumbledIncrementer" />
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement" />
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier" />
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter" />
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod" />
<rule ref="Generic.Commenting.Todo" />
<rule ref="Generic.Commenting.Fixme" />
<rule ref="Generic.ControlStructures.InlineControlStructure" />
<rule ref="Generic.Files.ByteOrderMark" />
<rule ref="Generic.Files.LineEndings" />
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
<property name="absoluteLineLimit" value="0"/>
</properties>
</rule>
<rule ref="Generic.Formatting.DisallowMultipleStatements" />
<rule ref="Generic.Formatting.NoSpaceAfterCast" />
<rule ref="Generic.Functions.CallTimePassByReference" />
<rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
<rule ref="Generic.Metrics.CyclomaticComplexity" />
<rule ref="Generic.Metrics.NestingLevel" />
<rule ref="Generic.NamingConventions.ConstructorName" />
<rule ref="Generic.NamingConventions.UpperCaseConstantName" />
<rule ref="Generic.NamingConventions.CamelCapsFunctionName" />
<rule ref="Generic.PHP.DeprecatedFunctions" />
<rule ref="Generic.PHP.DisallowShortOpenTag" />
<rule ref="Generic.PHP.ForbiddenFunctions" />
<rule ref="Generic.PHP.LowerCaseConstant" />
<rule ref="Generic.PHP.NoSilencedErrors" />
<rule ref="Generic.Strings.UnnecessaryStringConcat" />
<rule ref="Generic.WhiteSpace.DisallowTabIndent" />
<rule ref="Generic.WhiteSpace.ScopeIndent" />
<rule ref="MySource.PHP.EvalObjectFactory" />
<rule ref="MySource.PHP.GetRequestData" />
<rule ref="MySource.PHP.AjaxNullComparison" />
<rule ref="PEAR.Classes.ClassDeclaration" />
<rule ref="PEAR.Commenting.InlineComment" />
<rule ref="PSR1.Classes.ClassDeclaration" />
<rule ref="PSR1.Files.SideEffects" />
<rule ref="Squiz.PHP.CommentedOutCode" />
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops" />
<rule ref="Squiz.PHP.DiscouragedFunctions">
<properties>
<property name="error" value="true" />
</properties>
</rule>
<rule ref="Squiz.PHP.Eval" />
<rule ref="Squiz.PHP.GlobalKeyword" />
<rule ref="Squiz.PHP.Heredoc" />
<rule ref="Squiz.PHP.InnerFunctions" />
<rule ref="Squiz.PHP.LowercasePHPFunctions" />
<rule ref="Squiz.PHP.NonExecutableCode" />
<rule ref="Squiz.Scope.MemberVarScope" />
<rule ref="Squiz.Scope.MethodScope" />
<rule ref="Squiz.Scope.StaticThisUsage" />
<rule ref="Squiz.WhiteSpace.MemberVarSpacing" />
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing" />
<rule ref="Squiz.WhiteSpace.SemicolonSpacing" />
<!-- For Laravel migrations -->
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
<exclude-pattern>*/database/migrations/*</exclude-pattern>
</rule>
</ruleset>

13
phpunit.xml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" backupGlobals="false" backupStaticAttributes="false" colors="true" verbose="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory>src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Package">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

47
readme.md Normal file
View file

@ -0,0 +1,47 @@
# Exception Handler
[![Latest Version on Packagist][ico-version]][link-packagist]
[![Total Downloads][ico-downloads]][link-downloads]
[![Build Status][ico-travis]][link-travis]
[![StyleCI][ico-styleci]][link-styleci]
Creates sitemap.xml and refresh using a config file
## Installation
Via Composer
``` bash
$ composer require kwaadpepper/refresh-sitemap
```
## Usage
1 - php artisan handler:install
2 - Change configuration in config/refresh-sitemap.php
3 - put at least your team email in the above file in `contactsList` array
You can uninstall it by using php artisan handler:remove or juste revert changes in Exception/Handler.php
## Change log
Please see the [changelog](changelog.md) for more information on what has changed recently.
## Security
If you discover any security related issues, please email github@jeremydev.ovh instead of using the issue tracker.
## Credits
- [Jérémy Munsch][link-author]
- [All Contributors][link-contributors]
## License
MIT. Please see the [license file](license.md) for more information.
[ico-version]: https://img.shields.io/packagist/v/kwaadpepper/refresh-sitemap?style=flat-square
[ico-downloads]: https://img.shields.io/packagist/dt/kwaadpepper/refresh-sitemap?style=flat-square
[link-packagist]: https://packagist.org/packages/kwaadpepper/refresh-sitemap
[link-downloads]: https://packagist.org/packages/kwaadpepper/refresh-sitemap
[link-author]: https://github.com/kwaadpepper

View file

@ -0,0 +1,63 @@
<?php
namespace Kwaadpepper\RefreshSitemap\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Kwaadpepper\RefreshSitemap\Exceptions\SitemapException;
use Kwaadpepper\RefreshSitemap\Lib\SitemapGenerator;
class RefreshSitemap extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'sitemap:refresh {--D|dry-run}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Refresh the sitemap.xml in public';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$sitemapGenerator = new SitemapGenerator();
$this->info('Generating Sitemap..');
try {
if ($this->option('dry-run')) {
$this->info($sitemapGenerator->generateSiteMap());
return 1;
}
$dest = \public_path('sitemap.xml');
if (File::exists($dest)) {
$this->info('public/sitemap.xml exist, will overwrite.');
}
$sitemapGenerator->writeToFile(\public_path('sitemap.xml'));
} catch (SitemapException $e) {
$this->error('An error occured while generating sitemap');
$this->error($e->getMessage());
Log::error($e->getMessage());
if (config('app.debug')) {
dump($e);
}
}
$this->info('A new sitemap.xml was generated');
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace Kwaadpepper\RefreshSitemap\Exceptions;
use Exception;
/**
* @inheritDoc
*/
class SitemapException extends Exception
{
}

View file

@ -0,0 +1,312 @@
<?php
namespace Kwaadpepper\RefreshSitemap\Lib;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Routing\Route as RoutingRoute;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Route;
use Kwaadpepper\Enum\BaseEnumRoutable;
use Kwaadpepper\RefreshSitemap\Exceptions\SitemapException;
use Kwaadpepper\RefreshSitemap\Traits\SitemapRouteBinder;
use Kwaadpepper\RefreshSitemap\Traits\SitemapRouteConditions;
use Kwaadpepper\RefreshSitemap\Traits\SitemapRouteInfos;
use ReflectionClass;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
final class SitemapGenerator
{
use SitemapRouteBinder;
use SitemapRouteConditions;
use SitemapRouteInfos;
/** @var \Spatie\Sitemap\Sitemap */
private $siteMap = null;
public function __construct()
{
$this->siteMap = Sitemap::create();
}
/**
* Generate the sitemap Xml
*
* @return string An xml file
* @throws \Kwaadpepper\RefreshSitemap\Exceptions\SitemapException
*/
public function generateSiteMap(): string
{
$this->generate();
return $this->siteMap->render();
}
/**
* Write sitemap into a file
*
* @param string $path
* @return \Spatie\Sitemap\Sitemap
* @throws \Kwaadpepper\RefreshSitemap\Exceptions\SitemapException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function writeToFile(string $path): Sitemap
{
$this->generate();
return $this->siteMap->writeToFile($path);
}
// --- PRIVATE METHODS ---
/**
* Actually generate the sitemap
*
* @return void
* @throws \Kwaadpepper\RefreshSitemap\Exceptions\SitemapException
*/
private function generate(): void
{
self::initConfig();
$this->assertRouteBinderIsCorrect();
$routeCollection = Route::getRoutes();
$generatedUris = [];
/** @var \Illuminate\Routing\Route $route */
foreach ($routeCollection as $route) {
if (self::routeShouldBeIgnored($route)) {
continue;
}
if (!self::routeHandlesGetMethod($route)) {
continue;
}
$this->handleRoute($route, $generatedUris);
}
foreach ($generatedUris as $smRules) {
$this->siteMap->add(Url::create($smRules[0])
->setLastModificationDate(Carbon::yesterday())
->setChangeFrequency($smRules[1])
->setPriority($smRules[2]));
}
}
/**
* Init variables from configuration
*/
private static function initConfig(): void
{
self::initConfigBinder();
self::initConfigConditions();
self::initConfigInfo();
}
private function handleRoute(RoutingRoute $route, array &$generatedUris)
{
/** @var array to be populated with models from DB to generate a route */
$rParams = [];
/** @var \array [lluminate\Routing\Route::signatureParameters] parameters signature from the controller */
$params = $route->signatureParameters();
/** @var \array [Illuminate\Routing\Route::parameterNames] parameters name from the registered route */
$pNames = $route->parameterNames();
$hasModels = false;
$this->handleParams($params, $pNames, $route, $rParams, $hasModels);
// If the route has no dynamic models as parameters
if (!count($params) or !$hasModels) {
$rName = $route->getName();
$generatedUris[] = [
route($route->getName()),
self::getRouteFrequency($rName),
self::getRoutePriority($rName)
];
} else { // If the route has dynamic models as parameters
$this->genRoute($route, $generatedUris, $rParams);
}
}
/**
* Handle all route params
*
* @param array $params
* @param array|null $pNames
* @param \Illuminate\Routing\Route $route
* @param array $rParams
* @param bool $hasModels
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
* @throws \ReflectionException
* @throws \InvalidArgumentException
* @throws \Kwaadpepper\RefreshSitemap\Exceptions\SitemapException
*/
private function handleParams(
array $params,
array $pNames = null,
RoutingRoute $route,
array &$rParams,
bool &$hasModels
) {
foreach ($params as $param) {
/** @var ReflectionClass|null $name */
$name = $param->getType() && !$param->getType()->isBuiltin()
? new ReflectionClass($param->getType()->getName())
: null;
/** @var string $pName $parameter name on route uri */
$pName = '';
// If the route has parameters and the controller accepts it
if (count($pNames) and \array_key_exists($param->getPosition(), $pNames)) {
// Get the controller parameter name for the route
$pName = $pNames[$param->getPosition()];
}
if (config('app.debug')) {
dump(\trans(
'Route name : :routeName, Param [type: :typeName, name: :paramName, binder: :hasBinder]',
[
'routeName' => $route->getName(),
'typeName' => $name ? $name->getName() : '',
'paramName' => $pName,
'hasBinder' => $this->hasRouteBinderParam($route, $pName) ? 'true' : 'false'
]
));
}
// If We have a custom binder for this route
if ($this->hasRouteBinderParam($route, $pName)) {
$hasModels = true;
$this->processWithRouteBinderParam($rParams, $route, $pName);
continue;
}
// If This route takes a model as param
if ($name and $name->isSubclassOf(Model::class)) {
$hasModels = true;
/** @var \string */
$modelClassName = $name->getName();
$this->setParamsForModel($rParams, $modelClassName, $pName);
continue;
}
}
}
/**
* Process route with binderParam
*
* @param RoutingRoute $route
* @param string $pName
* @return void
*/
private function processWithRouteBinderParam(array &$rParams, RoutingRoute $route, string $pName)
{
$o = $this->getRouteBinderParam($route, $pName);
/** @var \string */
$modelClassName = $o[0];
/** @var \string */
$routeParamName = $o[1];
$rfLCls = (new ReflectionClass($modelClassName));
switch (true) {
case $rfLCls->isSubclassOf(Model::class):
$this->setParamsForModel($rParams, $modelClassName, $pName, $routeParamName);
break;
case $rfLCls->isSubclassOf(BaseEnumRoutable::class):
$this->setParamForEnum($rParams, $rfLCls->getName(), $pName);
break;
default:
throw new SitemapException(trans('Unhandled class type :className', [
'className' => $rfLCls->getName()
]));
}
}
/**
* Set route param Array for Enums
*
* @param array $rParams
* @param string $enumClassName
* @param string $pName
* @return void
*/
private function setParamForEnum(array &$rParams, string $enumClassName, string $pName)
{
foreach (forward_static_call(sprintf('%s::toArray', $enumClassName)) as $enum) {
if (!array_key_exists($pName, $rParams)) {
$rParams[$pName] = [];
}
$rParams[$pName][] = $enum->value;
}
}
/**
* Set route param Array for Models
*
* @param array $rParams
* @param string $modelClassName
* @param string $pName
* @param string $routeParamName
* @return void
*/
private function setParamsForModel(
array &$rParams,
string $modelClassName,
string $pName,
string $routeParamName = null
) {
$i = 0;
$limit = 10;
/** @var string $modelTable */
$modelTable = (new ReflectionClass($modelClassName))->newInstanceWithoutConstructor()->getTable();
$q = $modelClassName::query();
self::queryConditions($q, $modelTable);
do {
$results = $q->offset($i * $limit)->limit($limit)->get();
$length = count($results);
$results->map(function (Model $model) use (&$rParams, $pName, $routeParamName) {
if (!array_key_exists($pName, $rParams)) {
$rParams[$pName] = [];
}
$rParams[$pName][] = $routeParamName ? $model[$routeParamName] : $model;
});
$i++;
} while ($length);
}
/**
* Recursively generate routes for all params
*
* @param RoutingRoute $route
* @param array $generatedUris
* @param array $params
* @return void
*/
private function genRoute(RoutingRoute $route, array &$generatedUris, array $params)
{
$filtered = false;
foreach ($params as $pName => $param) {
if (is_array($param)) {
collect($param)->map(function ($p) use ($route, &$generatedUris, $params, $pName) {
$this->genRoute($route, $generatedUris, array_merge($params, [$pName => $p]));
});
return;
}
$filtered = array_key_last($params) === $pName ? true : false;
}
if ($filtered) {
$routeName = $route->getName();
$generatedUris[] = [
route($routeName, $params),
self::getRouteFrequency($routeName),
self::getRoutePriority($routeName)
];
}
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Kwaadpepper\RefreshSitemap;
use Illuminate\Support\ServiceProvider;
class RefreshSitemapServiceProvider extends ServiceProvider
{
protected $commands = [
'Kwaadpepper\RefreshSitemap\Console\Commands\RefreshSitemap'
];
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__ . '/../config' => config_path(),
], 'config');
}
public function register()
{
$this->mergeConfigFrom(
sprintf('%s/../config/refresh-sitemap.php', __DIR__),
'refresh-sitemap'
);
$this->commands($this->commands);
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace Kwaadpepper\RefreshSitemap\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Routing\Route as RoutingRoute;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;
use Kwaadpepper\Enum\BaseEnumRoutable;
use Kwaadpepper\RefreshSitemap\Exceptions\SitemapException;
use ReflectionClass;
trait SitemapRouteBinder
{
/**
* List of route with its corresponding parameter
* arguments and models
*/
private static $routesBinder = [];
/**
* Init variables from configuration
*/
private static function initConfigBinder(): void
{
self::$routesBinder = \config('refresh-sitemap.routesBinder', self::$routesBinder);
}
/**
* Check if $route exists in routesBinder
*
* @param RoutingRoute $route
* @return boolean
*/
private static function hasRouteBinder(RoutingRoute $route): bool
{
return array_key_exists($route->getName(), self::$routesBinder);
}
/**
* Check if $route exists in routesBinder and has $param
*
* @param RoutingRoute $route
* @param string $param
* @return boolean
*/
private static function hasRouteBinderParam(RoutingRoute $route, string $param): bool
{
if (self::hasRouteBinder($route)) {
return array_key_exists($param, self::getRouteBinder($route));
}
return false;
}
/**
* Get the routeBinder
*
* @param RoutingRoute $route
* @return array
*/
private static function getRouteBinder(RoutingRoute $route): array
{
return self::$routesBinder[$route->getName()] ?? [];
}
/**
* Get the routeBinder param
*
* @param RoutingRoute $route
* @param string $param
* @return array
*/
private static function getRouteBinderParam(RoutingRoute $route, string $param): array
{
return self::$routesBinder[$route->getName()][$param];
}
/**
* Assert that routeBinder is correct
*
* @throws SitemapException
* @return void
*/
private static function assertRouteBinderIsCorrect()
{
/** @var \Illuminate\Routing\RouteCollection */
$routeCollection = Route::getRoutes();
foreach (self::$routesBinder as $routeName => $params) {
if (!$routeCollection->hasNamedRoute($routeName)) {
throw new SitemapException(trans('Route :routeName does not exists', [
'routeName' => $routeName
]));
}
foreach ($params as $pName => $p) {
if (count($p) != 2) {
throw new SitemapException(\trans(
// phpcs:ignore Generic.Files.LineLength.TooLong
'routesBinder :routeName parameter :parameterName does not have Model classname and parameter name',
[
'routeName' => $routeName,
'parameterName' => $pName
]
));
}
$rfCls = (new ReflectionClass($p[0]))->newInstanceWithoutConstructor();
if (is_subclass_of($rfCls, BaseEnumRoutable::class)) {
return;
}
if (!is_subclass_of($rfCls, Model::class)) {
throw new SitemapException(trans(
'Route ":routeName" parameter ":parameterName" first element must be a child of ":className"',
[
'routeName' => $routeName,
'parameterName' => $pName,
'className' => Model::class
]
));
}
if (!Schema::hasColumn($rfCls->getTable(), $p[1])) {
throw new SitemapException(\trans(
// phpcs:ignore Generic.Files.LineLength.TooLong
'":routeName" parameter ":parameterName" second element ":value" must refer to and existing database column of ":column"',
[
'routeName' => $routeName,
'parameterName' => $pName,
'value' => $p[1],
'column' => $p[0]
]
));
}
}
}
}
}

View file

@ -0,0 +1,83 @@
<?php
namespace Kwaadpepper\RefreshSitemap\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Routing\Route as RoutingRoute;
use Illuminate\Support\Facades\Schema;
trait SitemapRouteConditions
{
/**
* List eloquent condition to decide whether
* a route should appear in the sitemap or not
*/
private static $queryConditions = [];
/**
* List of routes that should be ignored
* when generating sitemap
*
* @var array
*/
private static $ignoreRoutes = [];
/**
* Init variables from configuration
*/
private static function initConfigConditions()
{
self::$ignoreRoutes = \config('refresh-sitemap.ignoreRoutes', self::$ignoreRoutes);
self::$queryConditions = \config('refresh-sitemap.queryConditions', self::$queryConditions);
}
/**
* Add query conditions using the config table
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $modelTable
* @return void
* @throws \InvalidArgumentException
*/
private static function queryConditions(Builder &$query, string $modelTable): void
{
foreach (self::$queryConditions as $tableColumn => $expectedValue) {
$query = Schema::hasColumn($modelTable, $tableColumn) ?
$query->where($tableColumn, $expectedValue) : $query;
}
}
/**
* Check if the route handles the HTTP GET method
*
* @param RoutingRoute $route
* @return boolean
*/
private static function routeHandlesGetMethod(RoutingRoute $route): bool
{
return in_array('GET', $route->methods());
}
/**
* Check if the actual route uri start with
* a partial uri from the table
* Check also if it uses middleware auth
*
* @param RoutingRoute $route
* @return boolean
*/
private static function routeShouldBeIgnored(RoutingRoute $route): bool
{
foreach (self::$ignoreRoutes as $iRoute) {
if (strpos($route->uri(), ltrim($iRoute, '/')) === 0) {
return true;
}
}
foreach ($route->middleware() as $middleware) {
if (strpos($middleware, 'auth') === 0) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Kwaadpepper\RefreshSitemap\Traits;
use Spatie\Sitemap\Tags\Url;
trait SitemapRouteInfos
{
private static $routeDefaultPriority = 0.5;
private static $routeDefaultFrequency = Url::CHANGE_FREQUENCY_DAILY;
private static $routePriorities = [];
private static $routeFrequencies = [];
private static $existingRoutefreqencies = [
Url::CHANGE_FREQUENCY_ALWAYS,
Url::CHANGE_FREQUENCY_HOURLY,
Url::CHANGE_FREQUENCY_DAILY,
Url::CHANGE_FREQUENCY_WEEKLY,
Url::CHANGE_FREQUENCY_MONTHLY,
Url::CHANGE_FREQUENCY_YEARLY,
Url::CHANGE_FREQUENCY_NEVER
];
/**
* Init variables from configuration
*/
private static function initConfigInfo(): void
{
self::$routeDefaultPriority = \config('refresh-sitemap.routeDefaultPriority', self::$routeDefaultPriority);
self::$routeDefaultFrequency = \config('refresh-sitemap.routeDefaultFrequency', self::$routeDefaultFrequency);
self::$routePriorities = \config('refresh-sitemap.routePriorities', self::$routePriorities);
self::$routeFrequencies = \config('refresh-sitemap.routeFrequencies', self::$routeFrequencies);
}
/**
* Get a route priority
* @param string $routeName
* @return string
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
private static function getRouteFrequency(string $routeName): string
{
foreach (self::$routeFrequencies as $rName => $rFreqency) {
if (\strpos($routeName, $rName) === 0) {
return \in_array($rFreqency, self::$existingRoutefreqencies) ?
$rFreqency : self::$routeDefaultFrequency;
}
}
return self::$routeDefaultFrequency;
}
/**
* Get a route priority
* @param string $routeName
* @return float
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
private static function getRoutePriority(string $routeName): float
{
foreach (self::$routePriorities as $rName => $rPriority) {
if (\strpos($routeName, $rName) === 0) {
// Is a float between 0 and 1 ? $rPriority : $routeDefaultPriority
$rPrio = \is_numeric($rPriority) ? \floatval($rPriority) : self::$routeDefaultPriority;
return (($rPrio <=> 0) === 1 and ($rPrio <=> 1) === -1) ? $rPrio : self::$routeDefaultPriority;
}
}
return self::$routeDefaultPriority;
}
}