Relative include (@inc) for Blade views

Submitted by davestewart - 8 years ago

Blade's path syntax is a little odd - it uses dots instead of slashes, and always requires you to start your paths from the views root. This Blade extension allows you to pass paths with slashes, and correctly resolves parent paths via the traditional ../ syntax. Lastly, the quoting of paths is optional.

// usage - in any view:
@inc('path/to/child')
@inc('../path/to/parent')
@inc(quotes/are/optional)

// installation - place this code in a service provider
Blade::extend(function($view, $compiler)
{
    // (?<!\w)(\s*)@inc(\s*\(.*\))
    $pattern  $compiler->createMatcher('inc');

	// get paths
	$viewPath = $compiler->getPath();
	$viewFolder = dirname($viewPath) . '/';
	$root = \Config::get('view.paths')[0] . '/';

	// replace all matches
	return preg_replace_callback($pattern, function($matches) use($root, $viewPath, $viewFolder)
	{
		// get all parameters
		list($match, $whitespace, $param) = $matches;

		// get the relative path parameter
		$param = preg_replace('%[\\(\\)\'""]%', '', $param);

		// resolve the absolute path
		$path = $viewFolder . $param . '.blade.php';
		$path = realpath($path);

		// check it exists
		if( ! $path )
		{
			throw new \ErrorException("Relative @include '$param' not found in template '$viewPath'");
		}

		// if we still have a real, absolute path, convert it to dot syntax, so Blade can compile it
		$expression = str_replace($root, '', $path);
		$expression	= str_replace('.blade.php', '', $expression);
		$expression	= str_replace('/', '.', $expression);

		// return the new php to the view
		return "$whitespace<?php echo \$__env->make('$expression', array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>";

	}, $view);
});