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!
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:
<domain info>/wordpress<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.
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();
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.
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.
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 new controller to handle WordPress views and make sure to disable Yii layouts, as WordPress will handle its own layouts.
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 a view/wp/index.php file for your WPController, which loads WordPress up all the way.
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.
// 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.
Finally, adjust your main layout to spit out your Wordpress header/footer around your Yii content:
get_header(); <?php // echos Yii content echo $content; <?php get_footer();
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.
Total 12 comments
@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.
Does wp-admin work with this solution? I really really hope so :s
Any help much appreciated
Does wp-admin login work with this setup? I really need it to :s
@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. :-)
This method does not apply to wp-admin. I tried to add to themes/twentyeleven/functions.php
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:
Autoload also gives warning on creating new post:
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?
@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. :-/
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!
@OceanWind: just started a new forum post to use as discussion of this article. I'll post my thoughts over there.
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:
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.
@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.
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
Leave a comment
Please login to leave your comment.