Part of the EllisLab Network
   
 
multiple sites, 1 codebase, using symlinks (with smarty)
Posted: 14 July 2007 01:27 AM   [ Ignore ]  
Grad Student
Avatar
Rank
Total Posts:  72
Joined  06-06-2007

Greetings all,

First, I’d like to say thanks for making a great framework, its flexible and fun to use! I’ve only been at it a few days but am very impressed. I even managed to get Smarty templates integrated with very little trouble. (not required I know but was my preference)

Now onto the meat ...

MY REQUIREMENTS

My project called for a specific configuration of sites and applications:

site #1: one application
site #2: has both frontend and backend applications
     
(will also operate on wildcard sub-domains)
site #3: uses ssl with one application
site #4: one application (internal access)

All 4 sites are heavily related ... and will often want to use the same models, libraries, helpers, views etc. Unfortunately CI doesn’t give me a place to store these resources cross-application, and I really didn’t feel comfortable editing or extending the core CI classes. That might have made upgrading the framework painful with later releases. I like the idea of just ‘dropping in’ a new system folder without worry.

MY HOMEWORK (B-)

So as I was looking into CI initially, I found a lot of posts about running multiple-application sites. Overall, I got the impression that this was something CI ‘could’ do with tweaking (depending on your specific sites/applications) but wasn’t particularly great at out of the box. Thankfully, CI is so easy to work with that there are a number of solutions to consider.

Option 1:

One alternative was to run all my apps out of the same application folder, and just give them unique controllers ... I considered this but thought it might be difficult to keep site #1 from calling site #2’s controllers by editing the urls. My sites needed to be fairly secure in this regard.

Option 2:

Another alternative was to modify the loader of the CI core classes to include a new search path, essentially adding a Library level where cross-application code goes. This would have been the most attractive option if I felt more comfortable editing core files. But I don’t ... ick! Hopefully CI adopts a similar solution officially in a later release.

Some related links on that method:

http://www.jaaulde.com/test_bed/CodeIgniter/modLoader/
http://codeigniter.com/forums/viewthread/49157/

And other options I won’t get into right now ...

MY BACKGROUND INFO

My situation is unique to me perhaps. I happen to own the box my sites are being hosted on, so all configuration options are available. My sites use the traditional php5, mysql5, apache2 on Free(as in beer)BSD. I can edit httpd.conf freely and create .htaccess files as needed.

MY SOLUTION

I found I was able to share a (parent) application folder (located outside each site’s root) with a specific site’s (local) application folder ... I did this by creating a series of symbolic links in each (local) application folder to represent the corresponding folders in my (parent) application folder ...

I literally matched symbolic links to parent folders exactly ... EXCEPT for the (local) application’s ‘controllers’ folder, which was really the meat of each website.

It might be easier to show you the following folder hierarchy I used:

application/
    
my global application folder, where most the code goes
CodeIgniter_1.5.4
/
    
my CI installs system folder and other files, I leave the version
    number so that I can link to a
new system in the index.php if
    
needed for testing
common
/
    
an apache directory alias called common lets me link to subfolders here
    called img
, js, css, file, etc. Having one folder with all my includes makes
    it easy to specify in an
.htaccess file that CI should leave links alone with
    the word common in it
.
Smarty_2.6.18/
    
my smarty install, contains the actual libraries
www_site1
/
    
the site root for site #1
www_site2/
    
the site root for site #2
www_site3/
    
the site root for site #3
www_site4/
    
the site root for site #4
 Signature 

Mac Pro 4x3ghz, 1.8TB HD, 4G RAM, Dual 20” Screens in the house ... *drool*

Profile
 
 
Posted: 14 July 2007 01:28 AM   [ Ignore ]   [ # 1 ]  
Grad Student
Avatar
Rank
Total Posts:  72
Joined  06-06-2007

A closer look at my (GLOBAL) application folder:

cache
    SMARTY SPECIFIC FOLDER with write access
config
    standard codeigniter
configs
    SMARTY SPECIFIC FOLDER
controllers
    NOT USED GLOBALLY
- EACH SITE HAS ITS LOCAL CONTROLLERS FOLDER
    FOLDER IS ACTUALLY
EMPTY
errors
    standard codeigniter
helpers
    standard codeigniter
hooks
    standard codeigniter
libraries
    contains a wrapper
class called MySmarty.php for smarty useage
models
    standard codeigniter
templates
    SMARTY SPECIFIC FOLDER
templates_c
    SMARTY SPECIFIC FOLDER with write access
views
    standard codeigniter

A closer look at my (LOCAL) application folders (EACH SITE HAS ITS OWN):

cache
    symbolic link to parent
config
    symbolic link to parent
configs
    symbolic link to parent
controllers
    ALL LOCAL SITE SPECIFIC CONTROLLERS GO HERE
    
default controller is index.php as set in CI's global config
errors
    symbolic link to parent
helpers
    symbolic link to parent
hooks
    symbolic link to parent
libraries
    symbolic link to parent
models
    symbolic link to parent
templates
    symbolic link to parent
templates_c
    symbolic link to parent
views
    symbolic link to parent

A closer look at my individual site’s ROOT folders

.htaccess
    
(see below)
index.php
    links to
(local) application folder and global system folder
    system folder can be changed in one place to reference a
new CI install
applications
    mostly contains symbolic links
, but has a legit controllers folder

     
A closer look at my sites individual .htaccess files:

Options +SymLinksIfOwnerMatch
    
<IfModule mod_rewrite.c>
    
RewriteEngine on
    RewriteCond
$1 !^(index\.php|common|robots\.txt)
    
RewriteRule ^(.*)$ /index.php/$1 [L]
</IfModule>


TWEAKAGE:

A few small edits I made along the way.

For SSL.

Since I have a single CI config file for multiple applications/sites I had to make a small change to the way the $config[base_url] was captured. It now prefixes the URL with the proper protocol if SSL is in use on that site.

$config['base_url']    = (isset($_SERVER['HTTPS']) ? 'https://' : 'http://').$_SERVER['HTTP_HOST']."/";

instead of

$config['base_url']    = "http://".$_SERVER['HTTP_HOST']."/";

For Smarty.

In my (global) applications autoload.php file, I add the following:

$autoload['libraries'] = array('mySmarty');

Then I create a class file in my (global) application/libraries folder (remember to edit the smarty path)

Found on the CI forums, thx to the author whoever you are!

if (!defined('APPPATH')) exit('No direct script access allowed');

require_once(
APPPATH . "../../Smarty_2.6.18/libs/Smarty.class.php");

/*
|==========================================================
| Code Igniter - by pMachine
|----------------------------------------------------------
| www.codeignitor.com
|----------------------------------------------------------
| Copyright (c) 2006, pMachine, Inc.
|----------------------------------------------------------
| This library is licensed under an open source agreement:
| www.codeignitor.com/docs/license.html
|----------------------------------------------------------
| File: libraries/Smarty.php
|----------------------------------------------------------
| Purpose: Wrapper for Smarty Templates
|==========================================================
*/

class MySmarty extends Smarty{

    
var $smarty;
    
    function
MySmarty()
    
{
        $this
->smarty = new Smarty();
        
$this->smarty->template_dir = APPPATH . "templates";
        
$this->smarty->compile_dir = APPPATH . "templates_c";
        
$this->smarty->cache_dir = APPPATH . "cache";
        
$this->smarty->config_dir = APPPATH . "configs";
        
$this->smarty->compile_check = true;
        
$this->smarty->debugging = true;
        
log_message('debug', "Smarty Class Initialized");
    
}
    
    
function assign($key,$value)
    
{
        $this
->smarty->assign($key,$value);
    
}
    
    
function display($template)
    
{
        $this
->smarty->display($template);
    
}

}

I hope this is helpful for some, I appreciated having the forums as a resource to gather information from. CodeIgniter has a great community here.

Peace. smile

 Signature 

Mac Pro 4x3ghz, 1.8TB HD, 4G RAM, Dual 20” Screens in the house ... *drool*

Profile
 
 
Posted: 03 August 2007 12:00 AM   [ Ignore ]   [ # 2 ]  
Summer Student
Total Posts:  3
Joined  07-25-2007

Hi
I use smarty too for template and I like to learn how people are with it and dealing with situations that is covered by standard CI but probably have to handle separately when using with Smarty.
An example is form.

BTW,why do you need to re-declare assign and display but not adding anything extra? since the class extends smarty it should be available directly. Am I missing something?

Profile
 
 
Posted: 03 August 2007 12:09 AM   [ Ignore ]   [ # 3 ]  
Grad Student
Avatar
Rank
Total Posts:  72
Joined  06-06-2007
openology - 03 August 2007 12:00 AM

Hi
I use smarty too for template and I like to learn how people are with it and dealing with situations that is covered by standard CI but probably have to handle separately when using with Smarty.
An example is form.

BTW,why do you need to re-declare assign and display but not adding anything extra? since the class extends smarty it should be available directly. Am I missing something?

Well, I prefer to autoload my smarty wrapper as i have completely replaced CI’s view engine with it.

In your controller, you can load your variables into smarty as such:

$this->mysmarty->assign('sports', array(
    
'10' => 'Baseball',
    
'20' => 'Soccer',
    
'30' => 'Football'));

and then call something like:

$this->mysmarty->display('template.tpl');
 Signature 

Mac Pro 4x3ghz, 1.8TB HD, 4G RAM, Dual 20” Screens in the house ... *drool*

Profile
 
 
Posted: 08 August 2007 06:27 AM   [ Ignore ]   [ # 4 ]  
Grad Student
Avatar
Rank
Total Posts:  64
Joined  08-01-2007

fyi, trying something similar myself… found the logging messages a bit difficult as they all get dumped into the same log file no matter what site you are looking at.

Therefore I took the Log.php library and made a simple edit around line 92:

from

$filepath = $this->log_path.'log-'.date('Y-m-d').EXT;

to

$filepath = $this->log_path.$_SERVER['HTTP_HOST'].'-log-'.date('Y-m-d').EXT;

EDIT: typo on log.php.. DOH!

 Signature 


http://www.capitalh.net
freelance web developer in london

Profile
 
 
Posted: 08 August 2007 09:54 AM   [ Ignore ]   [ # 5 ]  
Grad Student
Avatar
Rank
Total Posts:  72
Joined  06-06-2007

Oooo nice idea. I’m going to use that smile Thanks.

 Signature 

Mac Pro 4x3ghz, 1.8TB HD, 4G RAM, Dual 20” Screens in the house ... *drool*

Profile
 
 
Posted: 24 October 2007 07:40 AM   [ Ignore ]   [ # 6 ]  
Summer Student
Avatar
Total Posts:  22
Joined  10-24-2007

I’ve been going through the forum checking best strategies for multi app executions and I must say, this is a sweet setup.
I’ve set everything up as outlined (dir structure, symlinks etc.) but I do have a few potential differences:

INDEX.PHP
My local installation is housed at ‘http://192.168.0.3/webapp’ with a path of ‘/htdocs/webapp’
Here is how I’ve setup my application switch. Notice I am using the URI to then go to the approriate app directory.

$myApp = '';
$uriArr = explode("/", $_SERVER['REQUEST_URI']);

switch(
$uriArr[2])
{
    
case 'cms':
        
$myApp = 'www_cms';
        break;

    case
'id':
        
$myApp = 'www_id';
        break;

    case
'test':
        
$myApp = 'application';
        break;

    default:
        
$myApp = 'application';
    
}
    
$application_folder
= $myApp;


PROBLEM
Issue is, whenever I then go to http://192.168.0.3/webapp/cms -OR- http://192.168.0.3/webapp/id even http://192.168.0.3/webapp/test I get a 404. http://192.168.0.3/webapp/ works fine.

I have not changed anything with the application constants in INDEX.PHP and my .htaccess (/htdocs/webapp) is as follows:

<IfModule mod_rewrite.c>
    
RewriteEngine On
    RewriteBase
/webapp
    RewriteCond
%{REQUEST_FILENAME} !-f
    RewriteCond
%{REQUEST_FILENAME} !-d
    RewriteRule
^(.*)$ index.php/$1 [L]
</IfModule>

<
IfModule !mod_rewrite.c>
    
ErrorDocument 404 /index.php
</IfModule>

.htaccess in my app folders (www_cms, www_id)

Options +FollowSymLinks

 

CLOSING
I’m wondering if there is a step I’m missing. Possibly with the .htaccess in /webapp -OR- the index.php file itself. What are your thoughts?

 Signature 

————————————————————————————————————————————————————————
+ +  Current inspiration
+

Artist: Telefon Tel Aviv | Song: 8 Track Project Cut | Album: Immediate Action (2002)
Listen to demo (LAST.FM)
————————————————————————————————————————————————————————

Profile
 
 
Posted: 24 October 2007 12:05 PM   [ Ignore ]   [ # 7 ]  
Summer Student
Avatar
Total Posts:  22
Joined  10-24-2007

SOLVED

The setup for multi sites using 1 codebase and symlinks works as is on the live server. I have my sub domains (cms.domain.com, id.domain.com) mirrored to the main domain.com. No files are being copied here it’s directly linked.

CHANGES IN INDEX.PHP (REMOTE SERVER - DOMAIN.COM)

// System folder var
$system_folder = "system_1.5.4";


// Application folder var
$myApp = '';

switch(
$_SERVER['HTTP_POST'])
{
    
case 'cms.domain.com':
        
$myApp = 'www_cms';
        break;

    case
'id.domain.com':
        
$myApp = 'www_id';
        break;

    default:
        
$myApp = 'application';
    
}
    
$application_folder
= $myApp;


// Application Constants
//Commented out the whole if(is_dir($application_folder)) biz and changed the APPPATH so I have just have the following:

define('EXT', '.'.pathinfo(__FILE__, PATHINFO_EXTENSION));
define('FCPATH', __FILE__);
define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));
define('BASEPATH', $system_folder.'/');
define('APPPATH', realpath($application_folder).'/');

 

FOR LOCAL SERVER
Had problems due to my use of the REQUEST_URI in order to do a switch for my $application_var locally. My work around for this is simple (...maybe not the best).

Instead of depending on ‘/htdocs/webapp/’ (http://192.168.0.3/webapp/) index.php file
to filter out the REQUEST_URI and switch the application_folder var. I placed separate index.php files in ‘/htdocs/webapp/www_cms’ and ‘/htdocs/webapp/www_id’. Instead of trying to access by pointing the brower to http://192.168.0.3/webapp/cms etc., I now access them directly (http://192.168.0.3/webapp/www_cms -OR- http://192.168.0.3/webapp/www_id)

LOCAL INDEX.PHP USED

// System folder var
$system_folder = "../system_1.5.4";

// Application folder var
$application_folder = '../www_cms'; -OR- $application_folder = '../www_id';

// Application constant
define('APPPATH', realpath($application_folder).'/');
 Signature 

————————————————————————————————————————————————————————
+ +  Current inspiration
+

Artist: Telefon Tel Aviv | Song: 8 Track Project Cut | Album: Immediate Action (2002)
Listen to demo (LAST.FM)
————————————————————————————————————————————————————————

Profile
 
 
Posted: 24 October 2007 12:21 PM   [ Ignore ]   [ # 8 ]  
Grad Student
Avatar
Rank
Total Posts:  72
Joined  06-06-2007

E1M2, Your solution for local server is exactly what I did on my live servers, separate index files and .htaccess files ... I liked having more granular control via separate master controller files. Glad this was of use to you, I’v been using the approach for a few months now and no issues.

For each of my 4 webroots i have the following structure:

.htaccess
application
/
favicon.ico
index
.php
robots
.txt
 Signature 

Mac Pro 4x3ghz, 1.8TB HD, 4G RAM, Dual 20” Screens in the house ... *drool*

Profile
 
 
Posted: 25 October 2007 11:05 AM   [ Ignore ]   [ # 9 ]  
Lab Assistant
RankRank
Total Posts:  218
Joined  10-25-2007

Just a note—your Smarty loader isn’t of the best design.

class MySmarty extends Smarty{

    
var $smarty;
    
    function
MySmarty()
    
{
        $this
->smarty = new Smarty();
        
$this->smarty->template_dir = APPPATH . "templates";
        
$this->smarty->compile_dir = APPPATH . "templates_c";
        
$this->smarty->cache_dir = APPPATH . "cache";
        
$this->smarty->config_dir = APPPATH . "configs";
        
$this->smarty->compile_check = true;
        
$this->smarty->debugging = true;
        
log_message('debug', "Smarty Class Initialized");
    
}
    
    
function assign($key,$value)
    
{
        $this
->smarty->assign($key,$value);
    
}
    
    
function display($template)
    
{
        $this
->smarty->display($template);
    
}

}

Note that when you call load->module(“MySmarty”), you are effectively creating a new object of MySmarty class, which is also creating a new object of Smarty class, which is already extended by MySmarty.

Effectively you are creating two instances of Smarty each time you load MySmarty—$this->MySmarty->cache_dir and $this->MySmarty->Smarty->cache_dir exist but are different values.

I would assume that the intention was to create one object (not having the ability to create multiple smarty objects).

This is what I use, based on some code I’ve run into and my own design:

class MySmarty extends Smarty {
    
    
function MySmarty(){
        $this
->Smarty();
        
        
$config =& get_config();
        
        
$this->template_dir = ! empty($config['smarty_template_dir']) ? $config['smarty_template_dir'] : BASEPATH."application/views/smarty/templates";
        
$this->compile_dir = ! empty($config['smarty_compile_dir']) ? $config['smarty_compile_dir'] : BASEPATH."application/views/smarty/templates_c";
        
$this->cache_dir = ! empty($config['smarty_cache_dir']) ? $config['smarty_cache_dir'] : BASEPATH."application/views/smarty/cache";
        
$this->config_dir = ! empty($config['smarty_config_dir']) ? $config['smarty_config_dir'] : BASEPATH."application/views/smarty/config";
        
        
//Eliminates URL Helper requirement
        
$CI =& get_instance();
        
$site_url = $CI->config->slash_item('base_url');

        
$this->assign("site_url",$site_url);
    
}

}

Load smarty as normal:
$this->load->module(“MySmarty”);
Use it as normal:
$this->MySmarty->assign()—etc.

Enjoy the reduction of memory usage per script.

Profile
 
 
Posted: 08 April 2008 10:37 PM   [ Ignore ]   [ # 10 ]  
Summer Student
Total Posts:  6
Joined  05-08-2007

I’m wondering if anybody using this setup as come up with a way to define separate routes for each site? Any ideas on how I might go about achieving this dream?

I have this solution setup using lighttpd and works great! Thanks for taking the time to share this with the community!

Profile
 
 
Posted: 09 April 2008 07:42 AM   [ Ignore ]   [ # 11 ]  
Grad Student
Avatar
Rank
Total Posts:  65
Joined  02-04-2008

I use a similar architecture with a single code base and sym links, etc. I’ve updated the index.php file for each site to include additional constant variables:

define('JASMINEROOT', $_SERVER['DOCUMENT_ROOT']);
define('JASMINEPATH', JASMINEROOT."/application");

The “/application” folder is an extremely slimmed version of a default “application” folder. I do not call mine “application” for security reasons but for this post I changed it for clarification purposes. This folder has a “views” folder for custom views for this site and a “config” folder. In the config folder I have all the config files from a standard CI install. In order for the custom config for each site to work I have to change the config files in the core code base “application/config” folder as follows:

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

if(
defined('JASMINEROOT')){include_once(JASMINEROOT.'/rabbit_hole/config/config.php');}

else{

/*

|--------------------------------------------------------------------------
| Base Site URL
|--------------------------------------------------------------------------
|
| URL to your CodeIgniter root. Typically this will be your base URL,
| WITH a trailing slash:
|
|    http://www.your-site.com/
|
*/
$config['base_url']    = "http://www.domain.com/";

/*
|--------------------------------------------------------------------------
| Index File
|--------------------------------------------------------------------------
|
| Typically this will be your index.php file, unless you've renamed it to
| something else. If you are using mod_rewrite to remove the page set this
| variable so that it is blank.
|
*/
$config['index_page'] = "index.php";

You can do this with any of your config files, including routes.php

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

if(
defined('JASMINEROOT')){include_once(JASMINEROOT.'/rabbit_hole/config/routes.php');}
else{
/*
| -------------------------------------------------------------------------
| URI ROUTING
| -------------------------------------------------------------------------
| This file lets you re-map URI requests to specific controller functions.
|
| Typically there is a one-to-one relationship between a URL string
| and its corresponding controller class/method. The segments in a
| URL normally follow this pattern:
|
|     www.your-site.com/class/method/id/
|
| In some instances, however, you may want to remap this relationship
| so that a different class/function is called than the one
| corresponding to the URL.
|
| Please see the user guide for complete details:
|
|    http://codeigniter.com/user_guide/general/routing.html
|
| -------------------------------------------------------------------------
| RESERVED ROUTES
| -------------------------------------------------------------------------
|
| There are two reserved routes:
|
|    $route['default_controller'] = 'welcome';
|
| This route indicates which controller class should be loaded if the
| URI contains no data. In the above example, the "welcome" class
| would be loaded.
|
|    $route['scaffolding_trigger'] = 'scaffolding';
|
| This route lets you set a "secret" word that will trigger the
| scaffolding feature for added security. Note: Scaffolding must be
| enabled in the controller in which you intend to use it.   The reserved
| routes must come before any wildcard or regular expression routes.
|
*/
$route['default_controller'] = "home";
$route['scaffolding_trigger'] = "";
}

?>

So in other words: I can load my codebase as a site in itself, or I can call it from another site. If I am calling it from another site, the constants are set and each config file will check for the constants and if exist, load the config files from the appropriate site.

Hope this helps!

 Signature 

“...‘Beyond Civilization’ isn’t a geographical space up in the mountains or on some remote desert isle. It’s a cultural space that opens up among people with new minds.” -Daniel Quinn, Beyond Civilization

Profile
 
 
Posted: 09 April 2008 08:48 PM   [ Ignore ]   [ # 12 ]  
Lab Assistant
Avatar
RankRank
Total Posts:  103
Joined  05-20-2006

Very interesting thread.  Thanks to all for investing the time on it.

We share some similarities….  We too have our own boxes colocated, but we have 6 servers.  And we also have some clients that prefer to run our code on their boxes and take care of their own hosting.  Oh, and we also have development machines (our own personal laptops), a testing/staging platform on Fedora Core 5, and then there’s all the production platforms.

The first challenge is in handling the migration from dev to test.  That’s not too bad because we all use Dreamweaver and we simply have the testing system as the deployment site and we just migrate all the files to that site.  But then when a rev has to be moved to production, we have scripts that we have written that FTP the files to a predetermined folder structure on each production environment.  Unfortunately the production environments can’t share a folder with common code, so we have to manually duplicate the entire CI framework on each server.  However if a single server runs multiple PHP apps that use CI, its conceivable that we could share a common code base there.  The problem isn’t technical, however.  The problem is that each application needs to be tested and validated before being moved to production, and if there is a common problem in CI that all share, then all are affected.  Its easier for us to validate each production system independently rather than hope that what works in one CI instance, will work just fine in all the other apps that share that code base.

Then it gets even more complex.  We have developers in multiple countries around the world.  They all have to check in their code to CVS before it gets moved to test.  This not only includes PHP code, but SQL scripts for Firebird (our chosen SQL database platform).  Also there are the HTML, Javascript, CSS and other files that all go together to make one system wholesome.  Oh, and yes we use Smarty extensively as well.

I’m not trying to overload this thread, but my suspicion is that the original poster may well be on a journey towards the same end as we are and may in fact encounter these configuration management issues at a bigger scale shortly.

Myles

 Signature 

Myles Wakeham
Director of Engineering
Tech Solutions USA, Inc.
Podcast Hosting at CyberEars.com

Profile
 
 
   
 
 
Post Marker Legend
New Topic New posts Hot Topic Hot Topic with new posts New Poll New Poll Moved Topic Moved Topic Sticky Topic Sticky topic
Old Topic No new posts Hot Old Topic Hot Topic with no new posts Old Poll Old Poll Closed Topic Closed Topic Announcement Announcements
Theme
Change Theme
Visitor Statistics
The most visitors ever was 719, on June 06, 2008 10:16 AM
Total Registered Members: 77571 Total Logged-in Users: 19
Total Topics: 101562 Total Anonymous Users: 2
Total Replies: 544409 Total Guests: 180
Total Posts: 645971    
Members ( View Memberlist )
Newest Members:  Idril616tonybernardcarterstarksColeJLinskitnealsemperjrawhallshiusbozzlynobluff