first working version
This commit is contained in:
commit
f17a33abec
19 changed files with 9619 additions and 0 deletions
18
.editorconfig
Normal file
18
.editorconfig
Normal 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
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
workspace.code-workspace
|
||||
vendor
|
||||
2
.styleci.yml
Normal file
2
.styleci.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
php:
|
||||
preset: laravel
|
||||
22
.vscode/launch.json
vendored
Normal file
22
.vscode/launch.json
vendored
Normal 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
6
changelog.md
Normal 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
45
composer.json
Normal 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
8562
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
82
config/refresh-sitemap.php
Normal file
82
config/refresh-sitemap.php
Normal 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
21
license.md
Normal 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
87
phpcs.xml
Normal 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
13
phpunit.xml
Normal 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
47
readme.md
Normal 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
|
||||
63
src/Console/Commands/RefreshSitemap.php
Normal file
63
src/Console/Commands/RefreshSitemap.php
Normal 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');
|
||||
}
|
||||
}
|
||||
12
src/Exceptions/SitemapException.php
Normal file
12
src/Exceptions/SitemapException.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\RefreshSitemap\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
class SitemapException extends Exception
|
||||
{
|
||||
}
|
||||
312
src/Lib/SitemapGenerator.php
Normal file
312
src/Lib/SitemapGenerator.php
Normal 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)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/RefreshSitemapServiceProvider.php
Normal file
34
src/RefreshSitemapServiceProvider.php
Normal 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);
|
||||
}
|
||||
}
|
||||
135
src/Traits/SitemapRouteBinder.php
Normal file
135
src/Traits/SitemapRouteBinder.php
Normal 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]
|
||||
]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/Traits/SitemapRouteConditions.php
Normal file
83
src/Traits/SitemapRouteConditions.php
Normal 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;
|
||||
}
|
||||
}
|
||||
73
src/Traits/SitemapRouteInfos.php
Normal file
73
src/Traits/SitemapRouteInfos.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue