File: D:/HostingSpaces/debout/debout.nl/app/Http/Middleware/WildcardResolver.php
<?php
namespace App\Http\Middleware;
use App\Http\Wildcards\WildcardInterface;
use App\Pages\Models\PageTranslation;
use App\Routes\Models\Route;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
final class WildcardResolver extends AbstractResolver
{
private static $wildcardFileNamespace = 'App\Http\Wildcards\\';
/**
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Check if route should be excluded from resolving
if ($this->isExcludedFromResolving($request)) {
return $next($request);
}
$this->log('trying to resolve uri: '.$request->path());
// Check if route is already resolved
if ($this->isRouteResolved($request)) {
$this->log('Request already resolved. Ignoring it. WildcardResolver done.');
return $next($request);
}
$wildcardRoute = $this->getWildcardRoute($request);
//The request did not represent a wildcard route. So pass the request further into the application and return that.
if (!$wildcardRoute) {
$this->log('Request was not a wildcard. Ignoring it. WildcardResolver done.');
return $next($request);
}
$wildcardTail = $this->getWildcardTail($request, $wildcardRoute);
$this->log('WildcardRoute tail: '.$wildcardTail);
// Create wildcard instance for type
$wildcardFQCN = $this->getWildcardFQCN($wildcardRoute->route.'Wildcard'); //The value of the route for example is "products".
/** @var WildcardInterface $wildcardInstance */
$wildcardInstance = new $wildcardFQCN;
//Duplicate the request so we can generate an restful Request
$modifiedRequest = $request->duplicate();
// Append original path, else have to done some ugly shit to grab it again
$modifiedRequest->attributes->add([
'original_path' => $request->url()
]);
// Let the wildcard instance handle the rest of resolving
$modifiedRequest = $wildcardInstance->handle($modifiedRequest, $wildcardRoute, $wildcardTail);
$this->log($wildcardFQCN.' Modified the request so that it\'s url became: '.$modifiedRequest->getUri());
//Set the resolved key on the request to tell other route solving things that they don't need to resolve
$modifiedRequest = $this->setRouteResolved($modifiedRequest);
return $next($modifiedRequest);
}
/**
* Use the request path (for example: /nl/producten/onderste-balk-plastic-aluminium) to find a wildcardRoute model
* That matches most of the url. E.g: /nl/producten.
* If it cannot be found, it will return null instead.
*
* @param Request $request
* @return Route|null
*/
private function getWildcardRoute(Request $request)
{
//request->segments() example value: [ 'nl', 'producten', 'onderste-balk-plastic-aluminium'] in an url of: http://localhost:8000/nl/producten/onderste-balk-plastic-aluminium
$firstSegments = $request->segments();
$wildcardRoutes = Route::where('route', 'not like', '%/%')->get(); //The "not like" leaves out the route records which contain forward slashes. These are alias routes.
$wildcardRoute = null;
//Pop the last element from the firstSegments array and put it in the currentSegment variable. Shortening $firstSegments by one. Use firstSegments array to find a wildcard route.
while ($currentSegment = array_pop($firstSegments) && $wildcardRoute == null) {
//First loop iteration example of value of firstSegments: ['nl', 'producten']
$route = '/' . implode('/', $firstSegments); //First loop iteration example value of route: /nl/producten
$this->log('Checking if there is a route with a route value of: "'.$route.'"');
if($route == '/') continue; // The route / never is a wildcard. So continue to a next iteration.
//Check if the route can be found. If not and if we still have firstSegments, run the next loop iteration.
$wildcardRoute = $wildcardRoutes->where('alias', '=', $route)->first(); //The "not like" leaves out the route records which contain forward slashes. These are alias routes.
}
if($wildcardRoute) $this->log('Wildcard Route found for route: "'.$wildcardRoute->route.'". Id: "'.$wildcardRoute->id.'".');
else $this->log('No wildcard route found.');
return $wildcardRoute;
}
/**
* Strips the wildcardRoute's route from the request. Whats left over is the tail.
* and that's what is going to be returned.
*
* Example: Consider that the request path is: /nl/producten/onderste-balk-plastic-aluminium
* And that the wildcardRoute's route is /nl/producten.
* The tail part that is going to be returned will be onderste-balk-plastic-aluminium.
*
* @param Request $request
* @param Route $wildcardRoute
* @return string
*/
private function getWildcardTail(Request $request, Route $wildcardRoute): string
{
return str_replace($wildcardRoute->alias.'/', '', '/'.$request->path());
}
/**
* Gets the fully qualified class name from a wild card name.
* For example when you pass 'Products' it will return 'App\Http\Wildcards\Products'
*
* @param string $wildcardClass
* @return string
*/
private function getWildcardFQCN(string $wildcardClass):string
{
$wildcardFQCN = static::$wildcardFileNamespace . ucfirst(strtolower($wildcardClass));
if(!self::wildcardExists($wildcardClass)) {
throw new \InvalidArgumentException($wildcardFQCN." does not exist or it wasn't an implementation of ".WildcardInterface::class);
}
return $wildcardFQCN;
}
/**
* Checks if a given name can be resolved to a FQCN in the PSR-4 wildcardFileNamespace path.
* Returns true if it exist and implements the WildcardInterface. false if not.
*
* @param string $wildcardClass
* @return bool
*/
public static function wildcardExists(string $wildcardClass) {
$wildcardFQCN = static::$wildcardFileNamespace . ucfirst(strtolower($wildcardClass));
if (!class_exists($wildcardFQCN) || !is_a($wildcardFQCN, WildcardInterface::class, true)) {
return false;
}
return true;
}
/**
* @param $codeName
* @return Route|null
*/
public static function getWildCardIndexRouteForPageWithCodeName($codeName)
{
$currentLanguage = app()->getLanguage();
$productPageTranslationForCurrentLanguage = PageTranslation::whereHas('translatable', function(Builder $query) use($codeName) {
$query->where('code_name', '=', $codeName);
})->where('language_id', '=', $currentLanguage->id)->first();
if(!$productPageTranslationForCurrentLanguage) return null;
$productWildcardIndexRoute = Route::where('alias', '=', '/'.$currentLanguage->iso_2.'/'.$productPageTranslationForCurrentLanguage->slug)->first();
return $productWildcardIndexRoute;
}
}