Overview ¶
A lot of people have written posts on integrating Yii and WordPress. This article combines goncin's approach with an article I read about integrating Symfony and WordPress and applies it all to Yii and WordPress.
When you're finished following this article, you'll have a website that integrates WordPress and Yii routes in under one path. For example, you can setup /widgets and /customers to point to Yii controllers and /about-us, /contact-us, /blog/* and /faq pointing to WordPress pages.
What we're going to do is place Yii in front of WordPress to act as a router/controller. We'll then load WordPress up partially underneath the hood (enough that Yii can use headers, etc) but not hand off to WordPress unless Yii can't resolve the route. If Yii can't resolve the route, we'll have WordPress act as a custom error handler (for 404s only). Having wrapped Yii around WordPress allows us to use Yii objects / models / databases within WordPress plugins, functions and themes. It's pretty cool!
GitHub repository ¶
The basic setup is available on Github: https://github.com/acorncom/yii-wordpress-integration
Note: there's a brief ReadMe on Github, but you may want to read on to understand what is going on :-)
WordPress setup ¶
The easiest way to set things up is to have WordPress in a separate folder within your webroot folder, so that you don't have to worry about WordPress upgrades overwriting your files (and especially your entry script). Once WordPress is installed and setup to run smoothly out of /wordpress (path is easy to change), make sure the General Settings for WordPress are setup as follows:
- WordPress Address (URL): http://`<domain info>`/wordpress
- Site Address (URL): http://`<domain info>`
Doing the above tells WordPress to set its links to all run through your Yii entry script but to set paths for images, stylesheets, js, etc. to your webroot/wordpress folder.
Yii entry script setup ¶
Add the below code to your entry script somewhere before you create your web application:
define('WP_USE_THEMES', true);
$wp_did_header = true;
require_once('wordpress/wp-load.php');
require_once(dirname(__FILE__) . '/../protected/components/ExceptionHandler.php');
$router = new ExceptionHandler();
....
require_once($yii);
Yii::createWebApplication($config)->run();
.htaccess file ¶
You'll also want to remove index.php from your path (if you haven't already). To do so, add a .htaccess file to your webroot folder with the following contents
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
and configure the showScriptName property of the urlManager component to be false.
There is more information on cleaning up your URLs in the guide, along with additional links.
Create our custom WordPress Exception handler ¶
The following code handles 404 errors using WordPress instead of the Yii 404 page. If there is indeed a 404 (because WordPress can't handle it either), the 404 page will be shown in the 404 layout in WordPress.
<?php
class ExceptionHandler
{
public function __construct()
{
define('YII_ENABLE_EXCEPTION_HANDLER',false);
set_exception_handler(array($this,'handleException'));
}
public function handleException($exception)
{
// disable error capturing to avoid recursive errors
restore_error_handler();
restore_exception_handler();
$event=new CExceptionEvent($this,$exception);
if($exception instanceof CHttpException && $exception->statusCode == 404)
{
try
{
Yii::app()->runController("wp/index");
}
catch(Exception $e) {}
// if we throw an exception in Wordpress on a 404, we can use
// our main error handler to handle the error
}
if(!$event->handled)
{
Yii::app()->handleException($exception);
}
}
}
?>
Create a WordPress controller ¶
Create a new controller to handle WordPress views and make sure to disable Yii layouts, as WordPress will handle its own layouts.
<?php
class WpController extends Controller
{
public function actionIndex()
{
$this->layout = false; // note that we disable the layout
try {
$this->render('index');
Yii::app()->end();
}
// if we threw an exception in a WordPress functions.php
// when we find a 404 header, we could use our main Yii
// error handler to handle the error, log as desired
// and then throw the exception on up the chain and
// let Yii handle things from here
// without the above, WordPress becomes our 404 error
// handler for the entire Yii app
catch (Exception $e) {
throw $e;
}
}
}
In addition, because we're using $this->render
, Yii::app()->clientScript
css/js files will be loaded into the WordPress header automatically after WordPress renders its content.
Create your controller view ¶
Create a view/wp/index.php file for your WPController, which loads WordPress up all the way.
<?php
wp();
require_once( ABSPATH . WPINC . '/template-loader.php' );
At this point, going to your main URL should display the Wordpress home page and (assuming you have logging on) the Yii log info beneath. You now have access to Yii models, are able to do renderPartials, run DAO commands, etc inside of Wordpress functions, plugins, page templates, etc.
Add some comment lines to your config file ¶
// Error handler
'errorHandler'=>array(
// use 'site/error' action to display errors
'errorAction'=>'site/error',
// the above is unused for 404 errors, as those
// are handled by Wordpress using our exception handler
),
Having WordPress handle 404 errors means you don't have to map every URL to WordPress (making adding new pages much easier for backend users). It also means we can prevent 404 headers from being sent by Yii (if you have WordPress handle errors as the errorHandler, things work properly, but a 404 header is sent for any WordPress page, which isn't ideal for SEO). NOTE: by having WordPress handle all errors, you don't ever see a 404 page inside of your Yii layout.
Setting up Yii views to be rendered in your Wordpress theme: ¶
Finally, adjust your main layout to spit out your Wordpress header/footer around your Yii content:
<?php
get_header(); ?>
<?php
// echos Yii content
echo $content;
?>
<?php get_footer(); ?>
Resources ¶
Questions / thoughts / feedback / suggestions? Please post to the forum topic linked above so it's easier for other people to find. Find some bugs / snags in the article? Comments below are gladly welcomed.
Very cool
I just followed your step-by-step instructions, and this seems to work great. It seems much simpler and less demanding on the server than some of the other solutions I've seen.
Not sure if you've explored this much, but since Wordpress is running through Yii, how is caching working? Is Yii's caching mechanism storing WordPress calls? Have you noticed any collisions with some of the more famous WordPress caching plugins? Or any other collisions?
Thanks for your work on this!
Bill
Re: how caching is working
@OceanWind / Bill: I believe caching could / would be handled by WordPress in the standard WordPress way (but I haven't tested it). There might be ways to have Yii cache an entire WordPress page (I think this would only work if you had a Yii action per WordPress page you were interested in), but I can see running into snags with invalidating the cached WordPress page in Yii. If a user updated a page in WordPress, you'd need to figure out how to tell Yii to stop caching that page.
Probably far simpler to use WordPress cache plugins :-)
And to answer your question re: other collisions, no, I haven't run into any so far, but I haven't tested it heavily yet. Let me know if you run into anything.
Permalinks and stylesheets
So far, I really like this approach. I haven't seen a significant degradation of performance, and that makes me very happy (probably because Yii is so lean and mean).
What I have noticed is that the error logs are recording that my permalinks won't resolve, even though the pages ultimately display:
2012/04/24 01:23:31 [error] [exception.CHttpException.404] exception 'CHttpException' with message 'Unable to resolve the request "sidebar-items/contact-sidebar".' in /home/lfs12/yii_framework/web/CWebApplication.php:280 Stack trace: #0 /home/lfs12/yii_framework/web/CWebApplication.php(135): CWebApplication->runController('sidebar-items/c...') #1 /home/lfs12/yii_framework/base/CApplication.php(162): CWebApplication->processRequest() #2 /home/lfs12/slowmoneycolorado.org/index.php(21): CApplication->run() #3 {main} REQUEST_URI=/sidebar-items/contact-sidebar/ HTTP_REFERER=http://slowmoneycolorado.org/main-menu/blog/
Any idea what's up with that and how we could get it to not register errors (or go through the added effort of generating an error before displaying)?
I also noticed that it was looking for the Yii stylesheets, so I created aliases to them from the WP Theme folder.
Posting my response to the forum
@OceanWind: just started a new forum post to use as discussion of this article. I'll post my thoughts over there.
Excellent tutorial
This is the best way to integrate Yii and WordPress. But please make it clear that URLs for WordPress pages do not start with "/wordpress" like some of the other methods.
This method seamlessly integrates Yii pages and WordPress pages into the same path.
Note: To get path routes working (ie. without the index.php?r=), you need to add a .htaccess to the webroot. Yii doesn't automatically create one when you build a new app.
Thanks!
Article updated
@exien, thanks for the suggestions, those additions have been added in. I've also been doing some research on the problem @OceanWind had and believe I've got an optimization for that that I can post in a few weeks. I'll need to test it first. It's a bit complicated. :-/
Very cool
This is very cool - I was thinking about doing something very similar with Drupal but was worried about overhead. I've done a few searches and can't find anything on the subject - has anyone tried?
Autoload problem in wp-admin
This method does not apply to wp-admin. I tried to add to themes/twentyeleven/functions.php
if (is_admin()) { global $yii,$config; if (is_null($yii)) $yii = ABSPATH . '../../yii/framework/yii.php'; require_once($yii); Yii::createApplication('CController'); Yii::app()->theme = null; $controller = new CController('site'); echo CHtml::link("asdf"); }
There's no way to create an Web Application here as autoload will always generates a fatal regarding YiiBase class (maybe because of wrong config), so I created a normal application with CController here instead.
The CHtml class works here, but the controller cannot render any views/text/file because of getTheme() error:
Fatal error: Call to a member function getTheme() on a non-object in C:\wamp\www\yii\framework\web\CController.php on line 638
Autoload also gives warning on creating new post:
Warning: include(_WP_Editors.php) [function.include]: failed to open stream: No such file or directory
Re: autoload problem
@kiennguyen: regarding your CHtml problem, did you copy/paste in your code? Because you have it mis-capitalized (should be CHtml, not Chtml) in your example.
Haven't tried a Wordpress admin function yet myself, but I think that uses a whole different Wordpress entry script. So we may need to duplicate some of the WpController stuff to run it inside of our Yii controller system.
Do let me know if you get it working. Would love to update the instructions. :-)
So wp-admin login doesn't work with this solution?
Does wp-admin login work with this setup? I really need it to :s
So wp-admin login doesn't work with this solution?
Does wp-admin work with this solution?
I really really hope so :s
Any help much appreciated
Re: So wp-admin login doesn't work with this solution?
@gvanto: yes, wp-admin logins work fine with this setup. Use /wordpress/wp-login.php If you have more problems, post over on the forum thread and we'll work it out there.
wp-admin
wp-admin works but you cant build stuff in Yii in the backend with this solution as it redirects to the wordpress installation directly.
I have not found a way around this yet.
problem with yii on adding wordpress
i have a problem in my event table,
there is a description column in my event table
the description has text hello "this" is my name
after adding wordpress
the description text comes like hello \"this" is my name
what can i do about it
WP Title and Sidebars
I went through and followed all your instructions. It doesn't appear that WP passes $post to yii so you can get the title, other page content as well as sidebars in the page. Any ideas on how to overcome this?
Thanks.
RE: WP Title and sidebars
@blupointmedia can you post on the forum post or a Github issue to discuss? Better to not use this page for troubleshooting issues. Thanks
RE: WP Title and Sidebars
@acorncom - Done. Thanks.
please help me understand :)
Hi,
thanks for your work.
"Yii entry script" is yii/framework/yiic.php ?
Re: entry script
@Bibi40k, no, Yii entry script meaning the index.php file that gets run initially. That help?
entry script
Thanks, it helped me understand which one is it and now more questions popped up :)
Here on this page you don't mention anything about index.php located into data folder as in github although inside you put
require_once('wordpress/wp-load.php'); require_once(dirname(__FILE__) . '/../protected/components/ExceptionHandler.php');
So wordpress folder and index.php are in the same folder (folder data as on github)
And you also said to set into WP config:
WordPress Address (URL): http://<domain info>/wordpress
Site Address (URL): http://<domain info>
So index.php and wordpress folder should be into root folder ...
I may be very tired and miss something... i'll check all the links inside scripts so they point proper path because in my case it still doesn't work.
Re: entry script
@Bibi40k, let's move this discussion onto the forum. Once we've worked out the issues, we can update the wiki article if needed. Thanks.
More explicit tutorial (for beginners)
i've created a tutorial and posted on the forum
HERE
loading yii widgets
Hi, how do i load widgets into WP posts ?
// Error handler
'errorHandler'=>array(
// use 'site/error' action to display errors 'errorAction'=>'site/error', // the above is unused for 404 errors, as those // are handled by Wordpress using our exception handler
),
Which file i paste this code, Please reply asap.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.