Extending default Laravel Packages

Service Container Based Extension

Almost every service provider included with the Laravel framework binds objects into the service container. You can find a list of your application’s service providers in the config/app.php configuration file. As you have time, you should skim through each of these provider’s source code. By doing so, you will gain a much better understanding of what each provider adds to the framework, as well as what keys are used to bind various services into the service container.

For example, the TranslationServiceProvider binds a translator key into the service container, which resolves into a Illuminate\Translation\Translator instance. You can easily extend and override this class within your own application by overriding this binding. For example:

Folders & Files

+ app/
+ bootstrap/
+ config/
- packages/
	+ foo/
	- laravel/
		- framework/
			- src/
				- Illuminate/
					- Translation/
						TranslationServiceProvider.php
						Translator.php
+ .../
+ vendor/
...
composer.json
...

The packages/.../Translator.php file content:

<?php

namespace Larapen\Translation;

use Illuminate\Contracts\Translation\Loader;

class Translator extends \Illuminate\Translation\Translator
{
	/**
	 * Create a new translator instance.
	 *
	 * @param  \Illuminate\Contracts\Translation\Loader $loader
	 * @param  string $locale
	 * @return void
	 */
	public function __construct(Loader $loader, $locale)
	{
		parent::__construct($loader, $locale);
	}
	
	/**
	 * Add translation lines to the given locale.
	 *
	 * @param  array $lines
	 * @param  string $locale
	 * @param  string $namespace
	 * @return void
	 */
	public function addLines(array $lines, $locale, $namespace = '*')
	{
		foreach ($lines as $key => $value) {
			list($group, $item) = explode('.', $key, 2);
			
			/*
			 * To accept strings with dots (like: 'Foo. Bar' or 'Foo.') as language files lines keys,
			 * 1. Limit the explode('.', KEY) line in Array::set() to 4.
			 * 2. Or replace the explode('.', KEY) line in Array::set() by a regex to split the KEY by dot.
			 *    NOTE: The dot will require character in left & right: (eg. 'foo.bar' instead of 'foo. bar' or 'foo.')
			 */
			self::arraySet($this->loaded, "$namespace.$group.$locale.$item", $value, 4);
		}
	}

	/**
	 * Set an array item to a given value using "dot" notation with a limit.
	 *
	 * If no key is given to the method, the entire array will be replaced.
	 *
	 * @param $array
	 * @param $key
	 * @param $value
	 * @param null $limit
	 * @return mixed
	 */
	private static function arraySet(&$array, $key, $value, $limit = null)
	{
		if (is_null($key)) {
			return $array = $value;
		}
		
		$keys = explode('.', $key, $limit);
		
		while (count($keys) > 1) {
			$key = array_shift($keys);
			
			// If the key doesn't exist at this depth, we will just create an empty array
			// to hold the next value, allowing us to create the arrays to hold final
			// values at the correct depth. Then we'll keep digging into the array.
			if (! isset($array[$key]) || ! is_array($array[$key])) {
				$array[$key] = [];
			}
			
			$array = &$array[$key];
		}
		
		$array[array_shift($keys)] = $value;
		
		return $array;
	}
}

The packages/.../TranslationServiceProvider.php file content:

<?php

namespace Larapen\Translation;

class TranslationServiceProvider extends \Illuminate\Translation\TranslationServiceProvider {
	
	public function register()
	{
		$this->registerLoader();
		
		$this->app->singleton('translator', function ($app) {
			$loader = $app['translation.loader'];
			
			// When registering the translator component, we'll need to set the default
			// locale as well as the fallback locale. So, we'll grab the application
			// configuration so we can easily get both of these values from there.
			$locale = $app['config']['app.locale'];
			
			$trans = new Translator($loader, $locale);
			
			$trans->setFallback($app['config']['app.fallback_locale']);
			
			return $trans;
		});
	}
}

Note that this class extends the TranslationServiceProvider, not the default ServiceProvider base class. Once you have extended the service provider, swap out the TranslationServiceProvider in your config/app.php configuration file with the name of your extended provider.

The config/app.php file content:

'providers' => [

    /*
     * Laravel Framework Service Providers...
     */
    Illuminate\Auth\AuthServiceProvider::class,
    Illuminate\Broadcasting\BroadcastServiceProvider::class,
    Illuminate\Bus\BusServiceProvider::class,
    Illuminate\Cache\CacheServiceProvider::class,
    Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
    Illuminate\Cookie\CookieServiceProvider::class,
    Illuminate\Database\DatabaseServiceProvider::class,
    Illuminate\Encryption\EncryptionServiceProvider::class,
    Illuminate\Filesystem\FilesystemServiceProvider::class,
    Illuminate\Foundation\Providers\FoundationServiceProvider::class,
    Illuminate\Hashing\HashServiceProvider::class,
    Illuminate\Mail\MailServiceProvider::class,
    Illuminate\Notifications\NotificationServiceProvider::class,
    Illuminate\Pagination\PaginationServiceProvider::class,
    Illuminate\Pipeline\PipelineServiceProvider::class,
    Illuminate\Queue\QueueServiceProvider::class,
    Illuminate\Redis\RedisServiceProvider::class,
    Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
    Illuminate\Session\SessionServiceProvider::class,
    Larapen\Translation\TranslationServiceProvider::class, // <--- Here
    Illuminate\Validation\ValidationServiceProvider::class,
    Illuminate\View\ViewServiceProvider::class,

    /*
     * Application Service Providers...
     */
    //...

The composer.json file content:

"autoload": {
	"psr-4": {
		"App\\": "app/",
		"Larapen\\Translation\\": "packages/laravel/framework/src/"
	}
}

Conclusion

This is the general method of extending any core class that is bound in the container. Essentially every core class is bound in the container in this fashion, and can be overridden. Again, reading through the included framework service providers will familiarize you with where various classes are bound into the container, and what keys they are bound by. This is a great way to learn more about how Laravel is put together.