Multisite with Codeigniter 3 | Database auto switch - php

Hello guys I need some help with multisite using Codeigniter.
So I was developing a multisite system where different companies register and they are given their sub-domain (E.g Company ABC go to www.system.com and register, then they are given sub-domain abc.system.com) then database abc (encrypted) is generated and default tables are created by the system.
All companies use the same interface, so the only thing change is the database.
My ideas was every time someone access abc.system.com I explode (abc.system.com) and I take abc encrypt it and I change DB connection in the config/database.php to
$db['company'] = array(
'database' => 'encrypted(abc)',
And User will use $this->db->query
But now when am thinking about it, disaster will happen when someone else happen to login to xyz.system.com because connection will change again to encrypted(xyz) because he/she will also be using $this->db->query
Remember the system is one and only database are changing.
I did not understand the technique of multisite using .htaccess
Now am stuck guys any ideas on how I can go around it?
Am using codeigniter 3.

I can recommend you to init this 2nd "company" DB inside a MY_Controller class which you can extend in all your Controllers (perhaps only those which are companies related.
By CI default configurations you are able to create MY_Conroller inside the core folder which will extend CI_Controller and any controller inside your controllers can extend it.
Inside this MY_Controller you will be able to parse the host (domain) and to set the right DB..
Example :
class My_Controller extends CI_Controller {
protected $dbC;
public function __construct(){
parent::__construct();
//HERE YOU CAN MAKE THE 2nd DB load after you detect that you are in valid subdomain
$expHost = explode('.', $_SERVER['HTTP_HOST']));
if( count($expHost) === 3 && $expHost !== 'www') {
$dbC_config = // Get your DB COMPANY configurations or input it here as array..
$dbC_config['database'] = 'encrypted('.$expHost[0].')';
$this->dbC = $this->load->database($dbC_config);
}
}
}

Related

Laravel - Dynamic APP_NAME config variable based on domain

How do I overwrite the APP_NAME config variable dynamically based on the domain/hostname so that I can reference it in all Controllers and Blade templates?
OR how can I create a global variable (single record from a Model) that changes dynamically based on the domain, so that it is accessible in all Controllers and Blade templates?
I have 3 domains - each for a different athletic conference/League - that share 100% the same code. Currently, I have a function in /app/Helper/Helper.php that I call from every Controller. This gets the correct League based on the domain, and allows me to send the correct set of data to each view.
// Get league info based on domain
public static function getLeague(Request $request)
{
$host = $request->getHost();
if (App::environment('production')) {
// Some fancy Substring logic to get the domain name
} else {
// Manually set $host to one of my domains for offline debugging
}
$league = League::where(['url' => $host])->first();
return $league;
}
The above works great for Views and Controllers I have built.
However, I also leverage several out-of-the-box views and email templates for User Registration, Login, Forgot Password, etc., that use APP_NAME.
Some of them (like the Login or Register screens) have a typical Controller so I can call my Helper function and just pass the League to the view. But for others, like email templates, I've tried going down the rabbit trail of Controllers and Functions that eventually produce the email content, and I cannot find where to call my Helper function and pass the League Name to the view.
An example of an email template using APP_NAME, that I cannot figure out how to pass $league to it instead of it using the config variable, is:
/vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/message.blade.php
So I'm stuck trying to figure out how to set the APP_NAME (or automatically calling my Helper function and creating a global $league variable that I can use in Controllers and Blade templates).
I have tried updating 'name' in /config/app.php, but it errors when trying to call the Helper function.
<?php
use App\Helper\Helper;
//dd($_SERVER['SERVER_NAME']);
$league = $league = Helper::getLeagueWithHost($_SERVER['SERVER_NAME']);
return [
'name' => $league->name,
....
]
Error:
Fatal error: Uncaught RuntimeException: A facade root has not been set. in C:\...\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php on line 258
This all wouldn't be a problem to set manually in each .env file, but I have an AWS CodePipeline that deploys to each domain upon GitHub push, so I'd have to update 3 .env files every time.
Thanks for any help!
I found a way to accomplish the ultimate goal, but not in the manner being requested in my initial question.
As part of the AWS CodePipeline deployment, there are "Appspec" events that fire at various times in the process (application-stop, before-install, after-install, application-start, and validate-service).
I wrote a Bash script in the "after-install" event that checks the IP of the hostname (returns the Private IP of the current Lightsail instance being deployed). And depending on the hostname IP, it changes line 2 of my .env file to the desired APP_NAME.
There is some hard-coding involved, mapping each Lightsail Private IP to an APP_NAME (meh), but as long as I do not delete and recreate a Lightsail instance, the Private IPs will remain static. The only way to avoid that would be to solve the initial question of updating the APP_NAME at runtime based on the URL/domain.
So, if someone else is trying to solve this problem at runtime when a page loads, this solution will not work for you.

Multiple sites by using only one code

when a user open any link of our application i need a controller by name site_check or any else which does the following things, firstly get site url opened exactly like "www.google.com" , i need to store the data like only google.com and if this value is checked with the database if exist.
if exist give a go add a local cookie of the same. Don't need to check this again until session is removed.
i will store the values like i need to pull the title logo and other static data from the platform to this.
how many possible ways are there we can try this to workout.
I am a new to this codeigniter and coding, needed your help.. how to start it I'm not getting any idea, please help me out.
Hi here is my solution and i have used it to develop single core multiple sites create a setting table save basic info like domain, site name and template information in that table add many setting later on, create MY_Controller under core directory check domain exist in your database or not
class MY_Controller extends CI_Controller {
public $site_id = 0;
public $template = '';
function __construct(){
parenet::__construct()
$this->loadSetting();
}
function loadSetting(){
$host = $_SERVER['HTTP_HOST'];
$row = $this->db->where_like('domain',$host)->get('setting')->row();
$this->site_id = $row->id;
$this->template = $row->template;
}
}
Once you setting is done you can add site_id as foreign key in your other tables like pages, news to get site specific pages and news and other information about site. Now extend your Page Controller or News Controller with MY_Controller like this.
class Page_Controller extends MY_Controller {
function __construct(){
parent::__construct();
}
function index(){
$page = $this->db->get_where('pages',array('site_id',$this->site_id))->row();
}
}
if you further want site_id available every where in your application you can create a library and autoload that library and you can put loadSetting method in that library. Like
class Setting {
private $ci;
public $site_id;
public $template;
function __construct(){
$this->ci = get_instance();
$this->loadSetting();
}
function loadSetting(){
$host = $_SERVER['HTTP_HOST'];
$row = $this->ci->db->where_like('domain',$host)->get('setting')->row();
$this->site_id = $row->id;
$this->template = $row->template;
}
}
Now you can access site_id in your application anywhere like models other libraries and controller like this
$this->setting->site_id;
hope this will solve your problem.
I believe you are trying to create Access Control layer for users in your organization to access certain websites which you want to permit. Though in a network environment behind NAT Server a simple proxy server like Microsoft ISA or any third party like Kerio can help you out by creating a simple rule
But if you want to do the same in Codeigniter, you need to startup with the following
Step-1
Create a Database table for Permitted sites like
id | website
----------------------------------
1 | facebook.com
2 | youtube.com
You need to have users table like
id | username | details
-------------------------------------
1 | Mudassar | php developer
You need to have a user-permission table like
id | user_id | site_id | permission
----------------------------------------
1 | 1 | 2 | allowed
Now Login your user
public function Login()
{
if($_POST)
{
// You can perform Validation here
$user_data=$this->Login_Model->checkUser($_POST);
if($user_data)
{
$this->session->set_userdata($user_data);
redirect(base_url().'user/dashboard'); // or whatever link
}
}
else
{
$this->load->view('login_view');
}
}
So now you have user_id in your session. Time to load your dashboard which supposedly have link to different websites
public function dashboard()
{
$data['sites']=$this->User_Model->getPermittedSites();
// Now you are only sending permitted sites to the user view
$this->load->view('dashboard',$data);
}
If you want to make things complicated, there are tons of ways but this is simplest.

Codeigniter how can I make this route work

I've looked around in the documentation and a few other places and I am not sure how to achieve the results I want. I have a domain name set up that will only server public profile pages to visitor. When someone comes straight to the site I want it to display a welcome screen telling more about what the website is about. (example.com) When someone visits example.com/my-public-page I want it to run a default controller that grabs the 'my-public-page from the url, searches the database for a user with that info in a column and displays their info.
I would image that I just set the default_controller to my controller and then check for a second string in the url. If its present search for that in the database. After looking around I am not sure how to make this work and not sure what to search for to get the results I need.
Thanks for your help,
After defining an encryption key for sessions in config.php you can use something like this:
class Wellcome extends CI_Controller {
public function __construct() {
parent::__construct();
$this -> load -> library('session');
if(!$this->session->userdata('visit_time')) {
$this->session->set_userdata('visit_time', time());
redirect('first_visit');
}
}
}

Detect when CodeIgniter fails to connect to a Db

I've got a CI instance which connects to a Db and checks permissions before serving pages. If the page isn't accessible to the current user, it redirects to a login page.
The login page, obviously, has permissions set so that it's accessible to all users.
After a recent glitch, the database server came back up on a different IP address (thank you Amazon, EC2). This resulted in CI being unable to check permissions for any page - including Login. Since the code assumes anything other than a Yes is a No, it redirected to Login. The result was an infinite redirect loop.
While this exact problem shouldn't happen again (Static elastic IP), I'd like to detect when the Db connection is down and handle it appropriately.
I've seen This SO Question which is what I'm trying to achieve but I'm not explicitly loading the database in any controllers, it's in the autoload config file.
So,
How can I query the state of the Db connection from inside CI? Do I have to run a useless query and check if I get results back or is there a more elegant solution?
Edit: The check is currently being performed in a hook:
$hook['post_controller_constructor'] = array(
'class' => 'AuthHook',
'function' => 'ValidateCredentials',
'filename' => 'auth.php',
'filepath' => 'hooks'
);
You can extend the controller and load the database in its constructor:
class My_Controller extends CI_Controller {
public function __construct(){
parent::__construct();
if ( $this->load->database() === FALSE )
{
//do something
}
}
}
All your controllers will inherit the new controller.
"Since the code assumes anything other than a Yes is a No, it redirected to Login."
So therefore you only need to alter the login logic function to specifically check for a DB connection (and thus still auto-load the database).
Use something like
$result = $this->db->conn_id;

Multiple Instances (2) of Zend_Auth

I have a CMS built on the Zend Framework. It uses Zend_Auth for "CMS User" authentication. CMS users have roles and permissions that are enforced with Zend_Acl. I am now trying to create "Site Users" for things like an online store. For simplicity sake I would like to use a separate instance of Zend_Auth for site users. Zend_Auth is written as a singleton, so I'm not sure how to accomplish this.
Reasons I don't want to accomplish this by roles:
Pollution of the CMS Users with Site Users (visitors)
A Site User could accidentally get elevated permissions
The users are more accurately defined as different types than different roles
The two user types are stored in separate databases/tables
One user of each type could be signed in simultaneously
Different types of information are needed for the two user types
Refactoring that would need to take place on existing code
In that case, you want to create your own 'Auth' class to extend and remove the 'singleton' design pattern that exists in Zend_Auth
This is by no means complete, but you can create an instance and pass it a 'namespace'. The rest of Zend_Auth's public methods should be fine for you.
<?php
class My_Auth extends Zend_Auth
{
public function __construct($namespace) {
$this->setStorage(new Zend_Auth_Storage_Session($namespace));
// do other stuff
}
static function getInstance() {
throw new Zend_Auth_Exception('I do not support getInstance');
}
}
Then where you want to use it, $auth = new My_Auth('CMSUser'); or $auth = new My_Auth('SiteUser');
class App_Auth
{
const DEFAULT_NS = 'default';
protected static $instance = array();
protected function __clone(){}
protected function __construct() {}
static function getInstance($namespace = self::DEFAULT_NS) {
if(!isset(self::$instance[$namespace]) || is_null(self::$instance[$namespace])) {
self::$instance[$namespace] = Zend_Auth::getInstance();
self::$instance[$namespace]->setStorage(new Zend_Auth_Storage_Session($namespace));
}
return self::$instance[$namespace];
}
}
Try this one , just will need to use App_Auth instead of Zend_Auth everywhere, or App_auth on admin's area, Zend_Auth on front
that is my suggestion :
i think you are in case that you should calculate ACL , recourses , roles dynamically ,
example {md5(siteuser or cmsuser + module + controller)= random number for each roles }
and a simple plugin would this role is allowed to this recourse
or you can build like unix permission style but i guess this idea need alot of testing
one day i will build one like it in ZF :)
i hope my idea helps you
You're mixing problems. (not that I didn't when I first faced id)
Zend_Auth answers the question "is that user who he claims to be"? What you can do is to add some more info to your persistence object. Easiest option is to add one more column into your DB and add it to result.

Categories