I'm using CodeIgniter to build a multilanguage web application. I have English and other languages under /system/languages/ folder and I've created a model responsible for changing the working language at run-time.
By default CodeIgniter is working in French as defined in /application/config/config.php
$config['language'] = 'french';
Later, according to a URI segment the model changes the language accordingly, simplified example bellow:
class multilang extends CI_Model {
public function __construct() {
parent::__construct();
if ($this->uri->segment(1) == 'en') {
$this->config->set_item('language', 'english');
}
}
}
This model is the first model listed under the auto load settings in /application/config/autoload.php and I can confirm that the language is indeed changed dynamically by calling:
echo $this->config->item('language');
However the built in form validation library does not take into account the changed language, instead only shows error messages from the language hard coded in the settings file /application/config/config.php in this case French.
At first I assumed this was because the form validation was loaded before the multilang model. To make sure the model was loaded first, I modified the form validation constructor to load the model before anything else like this:
public function __construct($rules = array())
{
$this->CI =& get_instance();
$this->CI->load->model('multilang');
// normal code after....
}
This made sure the model loaded before the form validation. Unfortunately this wasn't enough and the form validation still ignores the language when changed during run-time. Anyone knows why this happens?
Thank you.
The problem was that I was doing AJAX requests that didn't took into account the URI segment that contained the language abbreviation, because the URI for AJAX requests didn't needed the language segment in the first place, so I totally forgot about it.
Therefore I used the session cookie to store the language. Changing the multilang constructor to:
class multilang extends CI_Model {
public function __construct() {
parent::__construct();
# store lang between session
$data = $this->session->all_userdata();
if (isset($data['language'])) {
$lang = $data['language'];
# if lang was changed between sessions
if ($this->uri->segment(1) == 'fr'){
$lang = 'french';
} else if ($this->uri->segment(1) == 'en'){
$lang = 'english';
}
# if lang was changed using one of the lang abbreviations
# overule session setting
if ($this->uri->segment(1) == 'en') {
$lang = 'english';
} else if ($this->uri->segment(1) == 'fr') {
$lang = 'french';
}
$this->config->set_item('language', $lang);
$this->session->set_userdata('language', $lang);
} else {
if ($this->uri->segment(1) == 'en') {
$this->config->set_item('language', 'english');
$this->session->set_userdata('language', 'english');
} else if ($this->uri->segment(1) == 'fr') {
$this->config->set_item('language', 'french');
$this->session->set_userdata('language', 'french');
}
}
}
}
Note: The change to the form_validation constructor wasn't required.
Answer provided for future reference, and to remind people of little things we miss. It was so obvious right! Well this might help the next one who forgets.
Closing question.
Related
Before I added form helper I get all result and without error. But, when I call form helper I just get blank page without error.
Controller Script:
class Reza extends CI_Controller
{
function __construct()
{
parent::__construct();
$this->load->library(array("rez_lib"));
}
function index(){
$this->rez_lib->display("home");
}
}
library Script:
class Rez_lib
{
protected $_ci;
function __construct(){
$this->_ci =& get_instance();
}
function display($temp, $data=null){
$data['header'] = $this->_ci->load->view("construct/header",$data,true);
$data['sidebar'] = $this->_ci->load- >view("construct/sidebar",$data,true);
$data['content'] = $this->_ci->load->view("page/".$temp,$data,true);
$this->_ci->load->view("construct/all",$data,true);
}
}
First off the blank screen is because you do not have debugging enabled.
Go to index.php and set the 'ENVIRONMENT' constant to 'development' when developing.
Secondly it seems you have several typos in your library class.
In your constructor you have an extra space between & and get_instance(): $this->_ci =& get_instance();
In your class body you also have an extra space between load- and ->view (when loading your sidebar) $data['sidebar'] = $this->_ci->load- >view("construct/sidebar",$data,true);
Finally you are returning the view as data, since the third parameter is set to true. See:
There is a third optional parameter lets you change the behavior of the method so that it returns data as a string rather than sending it to your browser. This can be useful if you want to process the data in some way. If you set the parameter to TRUE (boolean) it will return data. The default behavior is false, which sends it to your browser. http://www.codeigniter.com/user_guide/general/views.html
I'm trying to generate ajax specific responses from my controllers by using the Request::ajax() method, which is working just fine. The only problem is that the way I have it set up right now isn't really a nice looking solution.
My controller:
class HomeController extends BaseController {
protected $layout = 'layouts/main';
public function __construct()
{
$this->beforeFilter('auth');
}
public function getIndex()
{
$view = View::make('content.home.index');
if(Request::ajax()) return $view; //For ajax calls we only want to return the content to be placed inside our container, without the layout
$this->layout->menu = 'content.menu';
$this->layout->content = $view;
}
}
So right now, for every method I define within my controllers I need to add the code snippet that checks for an AJAX request and returns a single view if the statement returns true.
This leads to my question that is probably more PHP related than it is to the framework;
Is there a way of executing my AJAX check on every method call, without actually placing it inside the method? Or is there some other solution to keep my code DRY?
Thanks in advance!
PS: This is my first post on stackoverflow, so feel free to correct me if I made any mistakes
Create a new barebone layout named 'layouts/ajax' (or any name you like).
<?php echo $content ?>
In your Base controller, override this setupLayout() function.
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$layout = Request::ajax() ? 'layouts/ajax' : $this->layout;
$this->layout = View::make($layout);
}
}
Change your getIndex() function to this.
public function getIndex()
{
$view = View::make('content.home.index');
$this->layout->menu = 'content.menu';
$this->layout->content = $view;
}
Now non-ajax requests will be rendered using layout set in the controller, where as ajax requests will receive whatever set to $this->layout->content.
Note : Controller will neglect the layout setup in setupLayout(), if the called method returns truthy value. So this method will not work for functions like below.
public function getIndex()
{
return View::make('content.home.index');
}
You could just change the layout property, in the constructor, if it's an ajax request:
public function __construct()
{
$this->beforeFilter('auth');
if(Request::ajax()) {
$this->layout = '';
}
}
If it doesn't work try setting it to NULL instead.
Why would you return a VIEW via ajax? Are you using it to create a SPA? If so there are better ways. I'm generally against returning HTML via AJAX.
The route I'd go in your position is probably opposite of how you're doing it. Render the view no matter what, if the request is ajax, pass the extra data back and have JS render the data on the page. That's essentially how most Javascript MVC frameworks function.
Sorry if I am totally missing the point here, just going on an assumption of your end goal with the info you provided.
I am trying to dynamically set database connection credentials based on who logs into a web page. I'm pretty sure it's not working because of the $connectdb variable not being defined. Can someone please check out my code and try to get it working? Thanks!
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$connectdb="";
class Main extends CI_Controller {
function __construct() {
parent::__construct();
echo $connectdb;
$this->load->database($connectdb);
$this->load->helper('url');
$this->load->library('grocery_CRUD');
}
public function index() {
if ($_POST["username"] == "root") {
global $connectdb="default";
}
if ($_POST["username"] == "user1") {
global $connectdb="user1";
}
if ($_POST["username"] == "user2") {
global $connectdb="user2";
}
$connect = #mysql_connect("localhost", $_POST["username"], $_POST["password"]);//won't display the warning if any.
if (!$connect) {
echo 'Server error. Please try again sometime. CON';
} else {
print("Employees");
echo "<br>";
print("Visitors");
}//Just an example to ensure that we get into the function
// LOAD LIBRARIES
}
public function employees() {
$this->grocery_crud->set_table('employees');
$output = $this->grocery_crud->render();
$this->_example_output($output);
}
public function visitors() {
$this->grocery_crud->set_table('visitors');
$output = $this->grocery_crud->render();
$this->_example_output($output);
}
function _example_output($output = null) {
$this->load->view('our_template.php',$output);
}
}
A quick read of THE MANUAL will show you it's pretty easy to have multiple database connections. You define the connection parameters in your database.php config file then call the database with the group name.
if($user == 'someguise'){
$this->load->database('guiseDB');
}
HTH
For something as important as this i would strongly suggest running the form input through CI form validation first. You really should be validating and doing things like limit the number of characters, make sure its letters only, trim whitespace and XSS cleaning - all before you do anything else. (this helps your user as well)
then to get the value from the form - do something like this
$username = $this->input->post( 'username', TRUE ) ;
and work with the one variable $username. the TRUE XSS cleans the value, and then instead of repeating
$_POST["username"] ==
over and over and over, you are just checking $username. also makes the code easier to read. if you need $username in different methods just use:
$this->username = $this->input->post( 'username', TRUE ) ;
and then $this->username will work in any method in the class.
finally consider having a table of users or config list -- and then use a different value to call your database. in other words maybe they log in with the user name: "root" but then its a different name like global $connectdb = "rootadmin"
edit
my solution in routes.php:
$route['news'] = 'news_controller';
$route['gallery'] = 'gallery_controller';
$route['(:any)'] = 'sites/$1';
and in my site conroller:
function index($site_id = '') {
//sanitize $site_id.
$this->site = $this->sites_model->get_site($site_id);
//etc.
}
THX to YAN
question:
so i wrote a little CMS with CodeIgniter. The admin can create sites. the site opens automatically when the segment of the url is like one in the DB. eg mysite.com/sites/about will call the "About" site. this works fine.
now i got a problem with my URL. i want this url
http://www.mysite.com/sites/about
turns to this:
http://www.mysite.com/about
the problem is, that i cannot use the routes.php and set wildcards for each site. (because they are dynamic and i dont know wich site the customer will create - and i dont want to edit the routes.php file for each site he will create - this should be done automatically)
the problem is i got other fix controllers too, like news, gallery or contact:
mysite.com/news, mysite.com/gallery, ...they work fine
so here is my Site Controller:
class Sites extends Public_Controller {
public $site;
public $url_segment;
public function _remap($method)
{
$this->url_segment = $this->uri->segment(2);
$this->load->model('sites_model');
$this->site = $this->sites_model->get_site($this->url_segment);
if($this->site !== FALSE)
{
$this->show_site($this->site);
}
else
{
show_404($this->url_segment);
}
}
public function show_site($data)
{
$this->template->set('site', FALSE);
$this->template->set('site_title', $data['name']);
$this->template->set('content',$data['content']);
$this->template->load('public/template','sites/sites_view', $data);
}}
and this is the Site_model who checks the database...if the url_segment fits the title in the DB:
class Sites_model extends CI_Model {
public function get_site($site_url)
{
if($site_url != ""){
$this->db->where('name', $site_url);
$query = $this->db->get('sites', 1);
if($query->num_rows() > 0){
return $query->row_array();
}
else
{
return FALSE;
}
}else{
return FALSE;
}
} }
i think i need something who checks if the controller exists (the first segment of the url) when not call the Site controller and check if the site is in the DB and when this is false then call 404.
any suggestions how this can be solved?
btw: sry for my english
regards GN
You can handle routes.php in the following way, just keep the (:any) value last:
$route['news'] = 'news_controller';
$route['gallery'] = 'gallery_controller';
$route['(:any)'] = 'sites/$1';
In your sites controller route to the specific site using the data from the URL.
function index($site_id = '') {
//sanitize $site_id.
$this->site = $this->sites_model->get_site($site_id);
//etc.
}
I am having trouble understanding the full intent of the question, but, from what I can tell, don't you simply need to add:
if ($this->uri->segment(1) != 'sites')
... // handle malformed URL
I have a function that needs to pull arguments from the URL like CI is supposed to do. But it's not doing it. My URL is domain.com/lasers/en/acme.
My class Lasers is:
class Lasers extends CI_Controller {
function __construct()
{
parent::__construct();
$this->load->model('products_model');
$this->load->model('common_model');
$this->load->model('select_country_model');
$this->load->model('markets_materials_model');
}
function index($lang = NULL, $laser = NULL)
{
$query = $this->products_model->get_product_content($laser, $lang);
}
The model is loaded in the constructor. The $lang I need is "en" and the $laser I need is "acme". So why isn't this working? The arguments in the function are in the correct order, so I can't see what's wrong.
By default you cant pass arguments to the index method of a controller
if you go to domain.com/lasers/en/acme it is looking in the lasers controller for a method called en.. (which doesn't exist) and trying to pass a single parameter of acme to it
Theres a few solutions, probly the easiest is to use a different method (not index) then use routes to make the URLs work.
add something like this to your config/routes.php
$route['^lasers/(:any)/(:any)'] = "lasers/get_products/$1/$2";
Then use a method like this instead of index:
function get_products($lang = NULL, $laser = NULL) {
$query = $this->products_model->get_product_content($laser, $lang);
}
.. OR you could use _remap to override the default behaviour
Does it work if you write "domain.com/lasers/index/en/acme"?
If you write domain.com/lasers/en/acme, it will look for the "En" function, $lang being "acme", and $laser remaining NULL.