I am building an application inside of Laravel. Part of this project is a front facing website that the marketing team at my company wants to be able to manage through WordPress. In order to allow them to do that, and in order to access that data easily, I am bootstrapping WordPress into Laravel in order to gain access to WordPress classes and functions.
WordPress bootstrapped in /public/index.php
<?php
/**
* Laravel - A PHP Framework For Web Artisans
*
* @package Laravel
* @author Taylor Otwell <taylorotwell@gmail.com>
*/
/*
|--------------------------------------------------------------------------
| Bootstrap Wordpress Inside Of Laravel
|--------------------------------------------------------------------------
|
| Brings in the wordpress functions for use inside of your laravel classes
| like your controllers and service providers. This makes true coupling
| inside of laravel possible.
|
*/
define('WP_USE_THEMES', false);
require __DIR__.'/hunchentoot/wp-blog-header.php';
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels nice to relax.
|
*/
require __DIR__.'/../bootstrap/autoload.php';
/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/
$app = require_once __DIR__.'/../bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
Unfortunately Artisan cant see those Classes when they're used inside of a service provider.
Service Provider In Question
<?php
namespace App\Providers;
use App\WPQuery\FooterLogosQuery;
use App\Formaters\FooterLogosFormater;
use Illuminate\Support\ServiceProvider;
class FooterLogosProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot(FooterLogosQuery $footerLogosQuery, FooterLogosFormater $footerLogosFormater)
{
$logosQuery = $footerLogosQuery->createQuery();
$queriedLogos = $footerLogosQuery->get_posts();
$logos = $footerLogosFormater->formatFooterLogos($queriedLogos);
view()->share('footerLogos', $logos);
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
Again to maintain my sanity, I abstracted away the WP_Query class into injectable classes inside of Laravel, below is the one thats being used by the service provider.
The "Query" For The Provider
<?php
namespace App\WPQuery;
class FooterLogosQuery extends \WP_Query
{
/**
* Container for the arguements for the query
*
* @var Array
*/
protected $args;
/**
* Container for the query order
*
* @var String
*/
public $order;
/**
* Container for the query post total
*
* @var Integer
*/
public $totalPosts;
/**
* Constructs the object and sets the default args
*
* @return void
*/
public function __construct()
{
$this->order = 'ASC';
$this->totalPosts = 6;
$this->setArgs();
}
/**
* Sets the order for the query if called
*
* @param String $order Container for the query order
*/
public function setOrder($order)
{
$this->order = $order;
$this->setArgs();
}
/**
* Sets the total for the query if called
*
* @param Integer $total Container for the query post total
*/
public function setTotalPosts($total)
{
$this->totalPosts = $total;
$this->setArgs();
}
/**
* (Re)Sets the arguements for the query upon call
*
* @return void
*/
public function setArgs()
{
$this->args = [
'post_type' => 'footer_logos',
'posts_per_page' => $this->totalPosts,
'order' => $this->order,
];
}
/**
* Creates the query for manipulation inside of controller
*
* @return Object This is a WordPress Query Object
*/
public function createQuery()
{
parent::__construct($this->args);
}
/**
* Resets the query once the objects use is complete
*
* @return void
*/
public function __destruct()
{
wp_reset_query();
}
}
Each of these also has a data formatter class so that way it can be cleanly passed between the controller and the view.
"Query" Formatter for the above class
<?php
namespace App\Formaters;
class FooterLogosFormater {
/**
* Create a new formater instance.
*
* @return void
*/
public function __construct()
{
}
/**
* Formats the information from the wordpress object
*
* @param Object $queryObject This is the raw Query Object
* @return Array Returns useable data to controller
*/
public function formatFooterLogos($queryObject)
{
$logos = [];
foreach($queryObject as $key => $logo)
{
$logoId = get_post_meta($logo->ID, 'footer_logo', true);
$logos[$key] = [
'logo_link' => get_post_meta($logo->ID, 'footer_logo_link', true),
'logo_image' => wp_get_attachment_url($logoId),
];
}
return $logos;
}
}
My this all works well, at least for the front end of the application. Artisan hates all of this, and I'm not sure what to do about it. Is there away to expose the global name space to artisan? Or am i just SOL?
Any help would be appreciated.
thanks, Robert
While this is not an ideal answer, I did find a work around.
New Service Provider
<?php
namespace App\Providers;
use App\WPQuery\FooterLogosQuery;
use App\Formaters\FooterLogosFormater;
use Illuminate\Support\ServiceProvider;
class FooterLogosProvider extends ServiceProvider
{
/**
* Loads the data for this service provider
*
* @return Array returns array of usable data
*/
public function loadData()
{
$footerLogosQuery = new FooterLogosQuery();
$footerLogosFormater = new FooterLogosFormater();
$logosQuery = $footerLogosQuery->createQuery();
$queriedLogos = $footerLogosQuery->get_posts();
$logos = $footerLogosFormater->formatFooterLogos($queriedLogos);
// Delets Objects From Memory
unset($footerLogosQuery, $footerLogosFormater);
return $logos;
}
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
$logos = [];
// Stops false flag from being thrown in artisan
if (!$this->app->runningInConsole())
{
$logos = $this->loadData();
}
view()->share('footerLogos', $logos);
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
Instead of using dependency injection, I manually insantiated new instances of the object for use inside of a secondary function and then restricted that functions execution to use when no on the CLI.