Laravel extend a vendor class installed from composer

001221 picture 001221 · Jul 15, 2015 · Viewed 11.8k times · Source

Hi I am using the following package https://github.com/jayhealey/Robots to add a different robots.txt file per environment I have on my application. However this is missing one method I am using on my application which is the crawl delay i.e.

Crawl-delay: 3600

Now i have the following folder from the composer install of this package:

vendor/healey/robots/src/Healey/Robots/Robots.php and this starts as so:

<?php namespace Healey\Robots;

class Robots
{....}

Now I want to be able to extend this class so i can use the method within it successfully, but obviously do not want to add the function within the vendor directory as this is undesirable. So I have created the following class:

app/lib/Helpers/AppRobots.php

and this has the following contents:

<?php
use Healey\Robots\Robots;
class AppRobots extends Robots {

    /**
     * Add crawl Delay to robots.txt.
     *
     * @param string $delay
     */
    public function crawlDelay($delay)
    {
        $this->addLine("Crawl-delay: $delay");
    }

}

Now in routes.php I have the following:

Route::get('robots.txt', function() {
    // If on the live server, serve a nice, welcoming robots.txt.
    if (App::environment() == 'production')
    {
        \Robots::addUserAgent('*');
        \AppRobots::crawlDelay('3600');

    } else {
        // If you're on any other server, tell everyone to go away.
        \Robots::addDisallow('*');
    }
    return Response::make(Robots::generate(), 200, array('Content-Type' => 'text/plain'));
});

Now this throws the following error:

Non-static method AppRobots::crawlDelay() should not be called statically

So i've changed the method to static as follows:

public static function crawlDelay($delay)
{
    $this->addLine("Crawl-delay: $delay");
}

However this then throws the following error:

Using $this when not in object context so I updated this to use the following method:

/**
 * Add crawl Delay to robots.txt.
 *
 * @param string $delay
 */
public static function crawlDelay($delay)
{
    $robots = new \Robots();
    $robots->addLine("Crawl-delay: $delay");
}

and now I get Call to undefined method Healey\Robots\RobotsFacade::addLine()

This is the RobotsFacade file (vendor/healey/robots/src/Healey/Robots/RobotsFacade.php)

<?php namespace Healey\Robots;

use Illuminate\Support\Facades\Facade;

class RobotsFacade extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'robots'; }
}

and this is the service provider (vendor/healey/robots/src/Healey/Robots/RobotsServiceProvider.php)

<?php namespace Healey\Robots;

use Illuminate\Support\ServiceProvider;

class RobotsServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Bootstrap the application events.
     *
     * @return void
     */
    public function boot()
    {
        $this->package('healey/robots');
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app['robots'] = $this->app->share(function($app)
        {
            return new Robots();
        });

        $this->app->booting(function()
        {
            $loader = \Illuminate\Foundation\AliasLoader::getInstance();
            $loader->alias('Robots', 'Healey\Robots\RobotsFacade');
        });
    }

}

Any ideas how I can successfuly extend this class so I can add an additional method as required?

update

app/lib/CustomRobotsServiceProvider.php

<?php 
namespace MyApp\Healey\Robots;
use Illuminate\Support\ServiceProvider;
class CustomRobotsServiceProvider extends ServiceProvider
{
  public function boot()
  {
  }

  public function register()
  {
    $this->app['robots'] = $this->app->share(function($app)
    {
        return new AppRobots();
    });
  }
}

app/lib/Helpers/AppRobots.php

<?php
namespace MyApp\Healey\Robots;
use MyApp\Healey\Robots\CustomRobotsServiceProvider;
use Healey\Robots\Robots as Robots;
class AppRobots extends Robots {

    /**
     * Add crawl Delay to robots.txt.
     *
     * @param string $delay
     */
    public static function crawlDelay($delay)
    {
        $robot = new Robots();
        $robot->addLine("Crawl-delay: $delay");
    }

}

app.php in the providers array, I have the following:

    'Healey\Robots\RobotsServiceProvider',
    'MyApp\Healey\Robots\CustomRobotsServiceProvider'

in the aliases array I have the following:

     'Robots'         => 'Healey\Robots\Robots',

However this is not adding the Crawl Delay line using this method:

        \Robots::crawlDelay('3600');

Any ideas why this line isn't been written to the robots.txt route? It is reaching the method fine but not successfully adding this line.

Answer

jedrzej.kurylo picture jedrzej.kurylo · Jul 15, 2015

You'll need to create your own service provider and overwrite the "robots" service there so that it uses your class, not the base one.

  1. Create a service provider

    use Illuminate\Support\ServiceProvider;
    class CustomRobotsServiceProvider extends ServiceProvider
    {
      public function boot()
      {
      }
    
      public function register()
      {
        $this->app['robots'] = $this->app->share(function($app)
        {
            return new AppRobots();
        });
      }
    }
    
  2. Register service provider in your config/app.php

    'providers' => array(
       ... some other providers
       'Your\Namespace\CustomRobotsServiceProvider'
    ),
    

Make sure your provider is registered after the RobotsServiceProvider so that your service overwrites the original one, not vice versa.