Step 6 – Creating categories for a multilanguage site in CodeIgniter

(created at: February 10, 2015; last update: April 13, 2015)
Well… This tutorial is in two versions. Let me explain why…

The first time I created the tutorial, back in 10 february 2015, I was still in some testing regarding a multilanguage CMS, but now, in april 2015, all seems a bit clearer. So, after I’ve created an admin area for pages, I returned to this tutorial about categories and I found myself saying “What the fuck was I thinking??? Packs??? What is thaaat??? A name column in categories table??? WHYYYYYY???”. If you still want to see that old tutorial, you can, as I’ve left it at the bottom of this new version.

And when I am saying “new version” I am actually saying that we should simply do the same as we did in the previous tutorial about creating, listing and editing pages. The only difference would be the name of the tables and replacing “page” or “pages” in the code with “category” and “categories”. Simple, no?

The tables

CREATE TABLE `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) unsigned NOT NULL DEFAULT '0',
  `order` int(4) unsigned NOT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `created_by` int(11) NOT NULL,
  `updated_by` int(11) NOT NULL,
  `deleted_by` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


CREATE TABLE `category_translations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category_id` int(11) NOT NULL,
  `language_slug` varchar(5) NOT NULL,
  `title` varchar(255) DEFAULT NULL,
  `menu_title` varchar(255) DEFAULT NULL,
  `page_title` varchar(100) DEFAULT NULL,
  `page_description` varchar(170) DEFAULT NULL,
  `page_keywords` varchar(255) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `created_by` int(11) NOT NULL,
  `updated_by` int(11) NOT NULL,
  `deleted_by` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `category_id` (`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

The models

<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Category_model extends MY_Model
{

    public function __construct()
    {
        $this->has_many['translations'] = array('Category_translation_model','category_id','id');
        $this->pagination_delimiters = array('<li>','</li>');
        $this->pagination_arrows = array('<span aria-hidden="true">&laquo;</span>','<span aria-hidden="true">&raquo;</span>');
        parent::__construct();
    }

    public $rules = array(
        'insert' => array(
            'parent_id' => array('field'=>'parent_id','label'=>'Parent ID','rules'=>'trim|is_natural|required'),
            'title' => array('field'=>'title','label'=>'Title','rules'=>'trim|required'),
            'menu_title' => array('field'=>'menu_title','label'=>'Menu title','rules'=>'trim'),
            'slug' => array('field'=>'slug', 'label'=>'Slug', 'rules'=>'trim'),
            'order' => array('field'=>'order','label'=>'Order','rules'=>'trim|is_natural|required'),
            'page_title' => array('field'=>'page_title','label'=>'Page title','rules'=>'trim'),
            'page_description' => array('field'=>'page_description','label'=>'Page description','rules'=>'trim'),
            'page_keywords' => array('field'=>'page_keywords','label'=>'Page keywords','rules'=>'trim'),
            'category_id' => array('field'=>'category_id', 'label'=>'Category ID', 'rules'=>'trim|is_natural|required'),
            'language_slug' => array('field'=>'language_slug','label'=>'language_slug','rules'=>'trim|required')
        ),
        'update' => array(
            'parent_id' => array('field'=>'parent_id','label'=>'Parent ID','rules'=>'trim|is_natural|required'),
            'title' => array('field'=>'title','label'=>'Title','rules'=>'trim|required'),
            'menu_title' => array('field'=>'menu_title','label'=>'Menu title','rules'=>'trim'),
            'slug' => array('field'=>'slug', 'label'=>'Slug', 'rules'=>'trim'),
            'order' => array('field'=>'order','label'=>'Order','rules'=>'trim|is_natural|required'),
            'page_title' => array('field'=>'page_title','label'=>'Page title','rules'=>'trim|required'),
            'page_description' => array('field'=>'page_description','label'=>'Page description','rules'=>'trim'),
            'page_keywords' => array('field'=>'page_keywords','label'=>'Page keywords','rules'=>'trim'),
            'translation_id' => array('field'=>'translation_id', 'label'=>'Translation ID', 'rules'=>'trim|is_natural_no_zero|required'),
            'category_id' => array('field'=>'category_id', 'label'=>'Category ID', 'rules'=>'trim|is_natural_no_zero|required'),
            'language_slug' => array('field'=>'language_slug','label'=>'language_slug','rules'=>'trim|required')
        )
    );
}
<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Category_translation_model extends MY_Model
{

    public function __construct()
    {
        $this->has_one['category'] = array('Category_model','id','category_id');
        parent::__construct();
    }
}

The Controller

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

class Categories extends Admin_Controller
{

	function __construct()
	{
		parent::__construct();
        if(!$this->ion_auth->in_group('admin'))
        {
            $this->session->set_flashdata('message','You are not allowed to visit the Categories page');
            redirect('admin','refresh');
        }
        $this->load->model('category_model');
        $this->load->model('category_translation_model');
        $this->load->model('slug_model');
        $this->load->model('language_model');
        $this->load->library('form_validation');
        $this->load->helper('text');
	}

	public function index()
	{
        $total_categories = $this->category_model->count();
        $list_categories = array();
        if($categories = $this->category_model->order_by('created_at, updated_at','desc')->with('translations')->paginate(30,$total_categories))
        {
            if(sizeof($categories)==1)
            {
                $list_categories[$categories->id] = array('created_at' => $categories->created_at, 'last_update' => $categories->updated_at, 'deleted' => $categories->deleted_at, 'translations' => array());
                if(isset($categories->translations)) {
                    foreach ($categories->translations as $translation)
                    {
                        $list_categories[$categories->id]['translations'][$translation->language_slug] = array('translation_id' => $translation->id, 'title' => $translation->title, 'created_at' => $translation->created_at, 'last_update' => $translation->updated_at, 'deleted' => $translation->deleted_at);
                        if ($translation->language_slug == $this->default_lang) {
                            $list_categories[$categories->id]['title'] = $translation->title;
                        } elseif (strlen($list_categories[$categories->id]['title']) == 0) {
                            $list_categories[$categories->id]['title'] = $translation->title;
                        }
                    }
                }
            }
            else
            {
                foreach ($categories as $category)
                {
                    $list_categories[$category->id] = array('created_at' => $category->created_at, 'last_update' => $category->updated_at, 'deleted' => $category->deleted_at, 'translations' => array(), 'title'=>'');
                    if(isset($category->translations))
                    {
                        foreach ($category->translations as $translation)
                        {
                            $list_categories[$category->id]['translations'][$translation->language_slug] = array('translation_id' => $translation->id, 'title' => $translation->title, 'created_at' => $translation->created_at, 'last_update' => $translation->updated_at, 'deleted' => $translation->deleted_at);
                            if ($translation->language_slug == $this->default_lang) {
                                $list_pages[$category->id]['title'] = $translation->title;
                            } elseif (strlen($list_categories[$category->id]['title']) == 0) {
                                $list_categories[$category->id]['title'] = $translation->title;
                            }
                        }
                    }
                }
            }
        }
        $this->data['categories'] = $list_categories;
        $this->data['next_previous_pages'] = $this->category_model->all_pages;
		$this->render('admin/categories/index_view');
	}

    public function create($language_slug = NULL, $category_id = 0)
    {
        $language_slug = (isset($language_slug) && array_key_exists($language_slug, $this->langs)) ? $language_slug : $this->current_lang;

        $this->data['content_language'] = $this->langs[$language_slug]['name'];
        $this->data['language_slug'] = $language_slug;
        $category = $this->category_model->get($category_id);
        if($category_id != 0 && $category==FALSE)
        {
            $category_id = 0;
        }
        if($this->category_translation_model->where(array('category_id'=>$category_id,'language_slug'=>$language_slug))->get())
        {
            $this->session->set_flashdata('message', 'A translation for that category already exists.');
            redirect('admin/categories', 'refresh');
        }
        $this->data['category'] = $category;
        $this->data['category_id'] = $category_id;
        $categories = $this->category_translation_model->where('language_slug',$language_slug)->order_by('menu_title')->fields('category_id,id,menu_title')->get_all();
        $this->data['parent_categories'] = array('0'=>'No parent category');
        if(!empty($categories))
        {
            foreach($categories as $category)
            {
                $this->data['parent_categories'][$category->category_id] = $category->menu_title;
            }
        }

        $rules = $this->category_model->rules;
        $this->form_validation->set_rules($rules['insert']);
        if($this->form_validation->run()===FALSE)
        {
            $this->render('admin/categories/create_view');
        }
        else
        {
            $parent_id = $this->input->post('parent_id');
            $title = $this->input->post('title');
            $menu_title = (strlen($this->input->post('menu_title')) > 0) ? $this->input->post('menu_title') : $title;
            $slug = (strlen($this->input->post('slug')) > 0) ? url_title($this->input->post('slug'),'-',TRUE) : url_title(convert_accented_characters($title),'-',TRUE);
            $order = $this->input->post('order');
            $page_title = (strlen($this->input->post('page_title')) > 0) ? $this->input->post('page_title') : $title;
            $page_description = $this->input->post('page_description');
            $page_keywords = $this->input->post('page_keywords');
            $category_id = $this->input->post('category_id');
            $language_slug = $this->input->post('language_slug');
            if ($category_id == 0)
            {
                $category_id = $this->category_model->insert(array('parent_id' => $parent_id, 'order' => $order, 'created_by'=>$this->user_id));
            }

            $insert_data = array('category_id'=>$category_id,'title' => $title, 'menu_title' => $menu_title, 'page_title' => $page_title, 'page_description' => $page_description,'page_keywords' => $page_keywords,'language_slug' => $language_slug,'created_by'=>$this->user_id);

            if($translation_id = $this->category_translation_model->insert($insert_data))
            {
                $this->category_model->update(array('parent_id'=>$parent_id, 'order'=>$order,'updated_by'=>$this->user_id),$category_id);
                $url = $this->_verify_slug($slug,$language_slug);
                $this->slug_model->insert(array(
                    'content_type'=>'category',
                    'content_id'=>$category_id,
                    'translation_id'=>$translation_id,
                    'language_slug'=>$language_slug,
                    'url'=>$url,
                    'created_by'=>$this->user_id));
                //$this->slug_model->where(array('content_type'=>'page','content_id'=>$page_id,'id !='=>$slug_id))->update(array('redirect'=>$slug_id));
            }

            redirect('admin/categories','refresh');

        }


    }

    public function edit($language_slug, $category_id)
    {
        $translation = $this->category_translation_model->where(array('category_id'=>$category_id, 'language_slug'=>$language_slug))->get();
        $this->data['content_language'] = $this->langs[$language_slug]['name'];
        if($translation == FALSE)
        {
            $this->session->set_flashdata('message', 'There is no translation for that category.');
            redirect('admin/categories', 'refresh');
        }
        $category = $this->category_model->get($category_id);
        if($category == FALSE)
        {
            $this->session->set_flashdata('message', 'There is no category to translate.');
            redirect('admin/categories', 'refresh');
        }
        $this->data['translation'] = $translation;
        $this->data['category'] = $category;
        $this->data['slugs'] = $this->slug_model->where(array('content_type'=>'category','translation_id'=>$translation->id))->get_all();
        $categories = $this->category_translation_model->where(array('language_slug'=>$language_slug,'category_id !='=>$category_id))->order_by('menu_title')->fields('category_id,id,menu_title')->get_all();
        $this->data['parent_categories'] = array('0'=>'No parent category');
        if(!empty($categories))
        {
            foreach($categories as $category)
            {
                $this->data['parent_categories'][$category->category_id] = $category->menu_title;
            }
        }

        $rules = $this->category_model->rules;
        $this->form_validation->set_rules($rules['update']);
        if($this->form_validation->run()===FALSE)
        {
            $this->render('admin/categories/edit_view');
        }
        else
        {
            $translation_id = $this->input->post('translation_id');
            if($translation = $this->category_translation_model->get($translation_id))
            {
                $parent_id = $this->input->post('parent_id');
                $title = $this->input->post('title');
                $menu_title = $this->input->post('menu_title');
                $slug = $this->input->post('slug');
                $order = $this->input->post('order');
                $page_title = (strlen($this->input->post('page_title')) > 0) ? $this->input->post('page_title') : $title;
                $page_description = $this->input->post('page_description');
                $page_keywords = $this->input->post('page_keywords');
                $category_id = $this->input->post('category_id');
                $language_slug = $this->input->post('language_slug');


                $update_data = array(
                    'title' => $title,
                    'menu_title' => $menu_title,
                    'page_title' => $page_title,
                    'page_description' => $page_description,
                    'page_keywords' => $page_keywords,
                    'updated_by' => $this->user_id);

                if ($this->category_translation_model->update($update_data, $translation_id))
                {
                    $this->category_model->update(array('parent_id' => $parent_id, 'order' => $order, 'updated_by' => $this->user_id), $category_id);
                    if(strlen($slug)>0)
                    {
                        $url = $this->_verify_slug($slug, $language_slug);
                        $new_slug = array(
                            'content_type' => 'category',
                            'content_id' => $category_id,
                            'translation_id' => $translation_id,
                            'language_slug' => $language_slug,
                            'url' => $url,
                            'created_by' => $this->user_id);
                        if($slug_id =  $this->slug_model->insert($new_slug))
                        {
                            $this->slug_model->where(array('content_type'=>'category', 'translation_id'=>$translation_id))->update(array('redirect'=>$slug_id,'updated_by'=>$this->user_id));
                        }
                    }
                    $this->session->set_flashdata('message', 'The translation was updated successfully.');
                }
            }
            else
            {
                $this->session->set_flashdata('message', 'There is no translation to update.');
            }
            redirect('admin/categories','refresh');
        }
    }
    private function _verify_slug($str,$language)
    {
        if($this->slug_model->where(array('url'=>$str,'language_slug'=>$language))->get() !== FALSE)
        {
            $parts = explode('-',$str);
            if(is_numeric($parts[sizeof($parts)-1]))
            {
                $parts[sizeof($parts)-1] = $parts[sizeof($parts)-1]++;
            }
            else
            {
                $parts[] = '1';
            }
            $str = implode('-',$parts);
            $this->_verify_slug($str,$language);
        }
        return $str;
    }
    public function delete($language_slug, $category_id)
    {
        if($category = $this->category_model->get($category_id))
        {
            if($language_slug=='all')
            {
                if($deleted_translations = $this->category_translation_model->where('category_id',$category_id)->delete())
                {
                    $deleted_slugs = $this->slug_model->where(array('content_type'=>'category','content_id'=>$category_id))->delete();
                    $deleted_categories = $this->category_model->delete($category_id);
                    $this->session->set_flashdata('message', $deleted_categories.' category deleted. There were also '.$deleted_translations.' translations and '.$deleted_slugs.' slugs deleted.');
                }
                else
                {
                    $deleted_categories = $this->category_model->delete($category_id);
                    $this->session->set_flashdata('message', $deleted_categories.' category was deleted');
                }
            }
            else
            {
                if($this->category_translation_model->where(array('category_id'=>$category_id,'language_slug'=>$language_slug))->delete())
                {
                    $deleted_slugs = $this->slug_model->where(array('content_type'=>'category','language_slug'=>$language_slug,'content_id'=>$category_id))->delete();
                    $this->session->set_flashdata('message', 'The translation and '.$deleted_slugs.' slugs were deleted.');
                }
            }
        }
        else
        {
            $this->session->set_flashdata('message', 'There is no translation to delete.');
        }
        redirect('admin/categories','refresh');

    }
}

The views

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<div class="container" style="margin-top:60px;">
    <div class="row">
        <div class="col-lg-12">
            <h1>Add Category in <?php echo strtolower($content_language);?></h1>
            <?php echo form_open('',array('class'=>'form-horizontal'));?>
            <div class="form-group">
                <?php
                echo form_label('Parent category','parent_id');
                echo form_dropdown('parent_id',$parent_categories,set_value('parent_id',(isset($category->parent_id) ? $category->parent_id : '0')),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Title','title');
                echo form_error('title');
                echo form_input('title',set_value('title'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Menu title','menu_title');
                echo form_error('menu_title');
                echo form_input('menu_title',set_value('menu_title'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Slug','slug');
                echo form_error('slug');
                echo form_input('slug',set_value('slug'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Order','order');
                echo form_error('order');
                echo form_input('order',set_value('order', (isset($page->order) ? $page->order : '0')),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Page title','page_title');
                echo form_error('page_title');
                echo form_input('page_title',set_value('page_title'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Page description','page_description');
                echo form_error('page_description');
                echo form_input('page_description',set_value('page_description'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Keywords','page_keywords');
                echo form_error('page_keywords');
                echo form_input('page_keywords',set_value('page_keywords'),'class="form-control"');
                ?>
            </div>
            <?php echo form_error('category_id');?>
            <?php echo form_hidden('category_id',set_value('category_id',$category_id));?>
            <?php echo form_error('language_slug');?>
            <?php echo form_hidden('language_slug',set_value('language_slug',$language_slug));?>
            <?php
            $submit_button = 'Add category';
            if($category_id!=0) $submit_button = 'Add translation';
            echo form_submit('submit', $submit_button, 'class="btn btn-primary btn-lg btn-block"');?>
            <?php echo anchor('/admin/pages', 'Cancel','class="btn btn-default btn-lg btn-block"');?>
            <?php echo form_close();?>
        </div>
    </div>
</div>
<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<div class="container" style="margin-top:60px;">
    <div class="row">
        <div class="col-lg-12">
            <h1>Edit Category in <?php echo strtolower($content_language);?></h1>
            <?php echo form_open('',array('class'=>'form-horizontal'));?>
            <div class="form-group">
                <?php
                echo form_label('Parent category','parent_id');
                echo form_dropdown('parent_id',$parent_categories,set_value('parent_id',$category->parent_id),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Title','title');
                echo form_error('title');
                echo form_input('title',set_value('title',$translation->title),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Menu title','menu_title');
                echo form_error('menu_title');
                echo form_input('menu_title',set_value('menu_title',$translation->menu_title),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Slug','slug');
                echo form_error('slug');
                echo form_input('slug',set_value('slug'),'class="form-control"');
                ?>
            </div>
            <?php
            echo '<div class="panel panel-primary">';
            echo '<div class="panel-heading">Currently active slugs</div>';
            echo '<div class="panel-body">';
            foreach($slugs as $slug)
            {
                echo $slug->url.'<br />';
            }
            echo '</div>';
            echo '</div>';
            ?>
            <div class="form-group">
                <?php
                echo form_label('Order','order');
                echo form_error('order');
                echo form_input('order',set_value('order', $category->order),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Page title','page_title');
                echo form_error('page_title');
                echo form_input('page_title',set_value('page_title',$translation->page_title),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Page description','page_description');
                echo form_error('page_description');
                echo form_input('page_description',set_value('page_description',$translation->page_description),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Keywords','page_keywords');
                echo form_error('page_keywords');
                echo form_input('page_keywords',set_value('page_keywords',$translation->page_keywords),'class="form-control"');
                ?>
            </div>
            <?php echo form_error('category_id');?>
            <?php echo form_hidden('category_id',set_value('category_id',$translation->category_id));?>
            <?php echo form_error('language_slug');?>
            <?php echo form_hidden('language_slug',set_value('language_slug',$translation->language_slug));?>
            <?php echo form_error('translation_id');?>
            <?php echo form_hidden('translation_id',set_value('translation_id',$translation->id));?>
            <?php
            $submit_button = 'Edit translation';
            echo form_submit('submit', $submit_button, 'class="btn btn-primary btn-lg btn-block"');?>
            <?php echo anchor('/admin/categories', 'Cancel','class="btn btn-default btn-lg btn-block"');?>
            <?php echo form_close();?>
        </div>
    </div>
</div>

And that’s it. BUT… Did you notice something about the Pages controller and the Categories controller? Is there something fishy in there? Please comment below if you notice what I am talking about. If you don’t know what I am talking about, I will tell you in the next episode.

Here you have an ARCHIVE WITH THE APPLICATION we’ve created so far. Take care as this archive is not containing a system directory. You should download a fresh copy of CodeIgniter 3, put all the files from the archive inside your fresh framework, set the database configuration and then import the ci_app.sql into your database.

Now… the old tutorial follows:

Once we’ve made our multilanguage site we must think about populating it with content. So let’s start by creating the categories for our posts.

How do we organize our tables

First thing to think about is the layout (schema) of our database. We already have a table named languages. So let’s think how we can set the tables so that they will allow for same content to be available in several languages. We will try to create tables that will be very comprehensive.

The packs table

In the pack table will have e some general data that is the same for all languages:

– an id column
– a type column (example of types: menu_items, categories, posts, pages)
– a parent_id column
order column (who knows, maybe we need to order the items)
– a created_at column
– an updated_at column
– a created_by column
– an updated_by column

Ok. Let’s create it:

CREATE TABLE `packs` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255) NOT NULL,
`order` int NOT NULL,
`type` varchar(100) NOT NULL,
`parent_id` int NOT NULL DEFAULT '0',
`created_at` datetime NULL,
`updated_at` datetime NULL,
`created_by` int NOT NULL,
`updated_by` int NOT NULL
) COMMENT='' ENGINE='InnoDB';

We should also add an index on name column.

Cool.

The categories table

Now let’s create the categories table. For this we will need the following columns:

– an id column
– a pack_id column
– a language_id column
– a name column
– a description column
– a slug column

And that’s about it…

So the MySQL will look like this:

CREATE TABLE `categories` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`pack_id` int NOT NULL,
`language_id` int NOT NULL,
`name` varchar(255) NOT NULL,
`description` text NULL,
`slug` varchar(255) NOT NULL
) COMMENT='' ENGINE='MyISAM';

Now, let’s do an index on slug column.

OK. Database ready for categories.

Now let’s look at our files

application/controllers/admin/Categories.php

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

class Categories extends Admin_Controller
{

	function __construct()
	{
		parent::__construct();
		$this->load->library('form_validation');
		$this->load->helper('form');
		$this->load->model('admin/category_model');
		$this->load->model('admin/language_model');
		$this->load->model('admin/pack_model');
	}

	public function index()
	{
		echo $this->session->flashdata('message');
		$this->data['languages'] = $this->language_model->get_all();
		$this->data['categories'] = $this->pack_model->get_all_categories_with_languages();
		$this->render('admin/categories/index_view');
	}

	public function create($pack_id = NULL, $language_id = NULL)
	{
		$pack_id = isset($pack_id) ? $pack_id : '0';
		$created_translations = array();
		if($pack_id != '0')
		{
			if($category_translations = $this->category_model->get_all(array('pack_id'=>$pack_id)))
			{
				foreach ($category_translations as $translation)
				{
					$created_translations[] = $translation->language_id;
				}
			}
		}
		$this->data['pack_id'] = $pack_id;
		if($pack_id != '0')
		{
			$pack = $this->pack_model->get_by_id($pack_id);
			$order = $pack->order;
			$parent_id = $pack->parent_id;
		}
		else
		{
			$order = '0';
			$parent_id = '0';
		}
		$this->data['order'] = $order;
		$this->data['parent_id'] = $parent_id;

		$langs = array('0'=>'No language set');
		$default_language = '0';
		if($languages = $this->language_model->get_all())
		{
			foreach($languages as $language)
			{
				if($language->default == '1') $default_language = $language->id;
				if(!in_array($language->id,$created_translations)) $langs[$language->id] = $language->language_name;
			}
		}
		if(isset($language_id)) $default_language = $language_id;
		$categs = array('0'=>'No parent category');
		if($categories  = $this->category_model->get_all(array('language_id'=>$default_language)))
		{
			foreach($categories as $category)
			{
				$categs[$category->pack_id] = $category->name;
			}
		}
		$this->data['languages'] = $langs;
		$this->data['categories'] = $categs;
		$this->data['default_language'] = $default_language;
		$this->form_validation->set_rules('language_id','Language','trim|is_natural|required|exists[languages.id]');
		$this->form_validation->set_rules('parent_id','Parent id','trim|is_natural|required');
		$this->form_validation->set_rules('name','Category name','trim|required');
		$this->form_validation->set_rules('description','Category description','trim');
		$this->form_validation->set_rules('slug','Category slug','trim');
		$this->form_validation->set_rules('order','Order','trim|is_natural|required');
		$this->form_validation->set_rules('pack_id','Category id','trim|is_natural|required');
		if($this->form_validation->run() === FALSE)
		{
			$this->render('admin/categories/create_view');
		}
		else
		{
			$datetime = date('Y-m-d H:i:s');
			$pack_id = $this->input->post('pack_id');
			$language_id = $this->input->post('language_id');
			$parent_id = $this->input->post('parent_id');
			$name = $this->input->post('name');
			$description = $this->input->post('description');
			$slug = (strlen($this->input->post('slug'))>0) ? url_title($this->input->post('slug'),'-',TRUE) : url_title($name,'-',TRUE);
			$order = $this->input->post('order');

			if($pack_id == '0')
			{
				$pack_id = $this->pack_model->create(array(
						'name' => $name,
						'order' => $order,
						'type' => 'category',
						'parent_id' => $parent_id,
						'created_at' => $datetime,
						'created_by' => $this->data['current_user']->id
						)
					);
			}
			$category_id = $this->category_model->create(array(
				'pack_id' => $pack_id,
				'language_id' => $language_id,
				'name' => $name,
				'description' => $description,
				'slug' => $slug
			));
			if($category_id)
			{
				$this->session->set_flashdata('message','Category added successfully');
			}
			redirect('admin/categories','refresh');
		}
	}

	public function edit($category_id = NULL)
	{
		$category_id = isset($category_id) ? $category_id : $this->input->post('category_id');
		$category = $this->category_model->get_by_id($category_id);
		$this->data['category'] = $category;

		$pack = $this->pack_model->get_by_id($category->pack_id);
		$order = $pack->order;
		$parent_id = $pack->parent_id;
		$this->data['order'] = $order;
		$this->data['parent_id'] = $parent_id;

		$translations = $this->category_model->get_all(array('pack_id'=>$category->pack_id));
		$translation_exists = array();
		foreach($translations as $translation)
		{
			$translation_exists[] = $translation->language_id;
		}

		$langs = array();
		if($languages = $this->language_model->get_all())
		{
			foreach($languages as $language)
			{
				if(!in_array($language->id, $translation_exists) || ($language->id == $category->language_id)) $langs[$language->id] = $language->language_name;
			}
		}
		$categs = array('0'=>'No parent category');
		if($categories  = $this->category_model->get_all(array('language_id'=>$category->language_id,'pack_id != '=>$category->pack_id)))
		{
			foreach($categories as $category)
			{
				$categs[$category->pack_id] = $category->name;
			}
		}
		$this->data['languages'] = $langs;
		$this->data['categories'] = $categs;
		$this->data['default_language'] = $category->language_id;
		$this->form_validation->set_rules('language_id','Language','trim|is_natural|required|exists[languages.id]');
		$this->form_validation->set_rules('parent_id','Parent id','trim|is_natural|required');
		$this->form_validation->set_rules('name','Category name','trim|required');
		$this->form_validation->set_rules('description','Category description','trim');
		$this->form_validation->set_rules('slug','Category slug','trim');
		$this->form_validation->set_rules('order','Order','trim|is_natural|required');
		$this->form_validation->set_rules('pack_id','Category id','trim|is_natural|required');
		if($this->form_validation->run() === FALSE)
		{
			$this->render('admin/categories/edit_view');
		}
		else
		{
			$datetime = date('Y-m-d H:i:s');
			$pack_id = $this->input->post('pack_id');
			$language_id = $this->input->post('language_id');
			$parent_id = $this->input->post('parent_id');
			$name = $this->input->post('name');
			$description = $this->input->post('description');
			$slug = (strlen($this->input->post('slug'))>0) ? url_title($this->input->post('slug'),'-',TRUE) : url_title($name,'-',TRUE);
			$order = $this->input->post('order');

			if($this->pack_model->update($pack_id, array('order' => $order,'parent_id' => $parent_id,'updated_at' => $datetime,'updated_by' => $this->data['current_user']->id)))
			{
				$category_id = $this->category_model->create(array(
					'pack_id' => $pack_id,
					'language_id' => $language_id,
					'name' => $name,
					'description' => $description,
					'slug' => $slug
				));
			}

			if($category_id)
			{
				$this->session->set_flashdata('message','Category translation updated successfully');
			}
			redirect('admin/categories','refresh');
		}
	}


	public function edit_pack($pack_id = NULL)
	{
		$pack_id = isset($pack_id) ? $pack_id : $this->input->post('pack_id');
		$pack = $this->pack_model->get_by_id($pack_id);

		$this->data['pack'] = $pack;
		$order = $pack->order;
		$parent_id = $pack->parent_id;
		$this->data['order'] = $order;
		$this->data['parent_id'] = $parent_id;

		$default_language = $this->language_model->get_all(array('default'=>'1'));

		$categs = array('0'=>'No parent category');
		if($categories  = $this->category_model->get_all(array('language_id'=>$default_language->id, 'pack_id != '=>$pack->id)))
		{
			foreach($categories as $category)
			{
				$categs[$category->pack_id] = $category->name;
			}
		}
		$this->data['categories'] = $categs;
		$this->data['default_language'] = $category->language_id;
		$this->form_validation->set_rules('parent_id','Parent id','trim|is_natural|required');
		$this->form_validation->set_rules('name','Category name','trim|required');
		$this->form_validation->set_rules('order','Order','trim|is_natural|required');
		$this->form_validation->set_rules('pack_id','Category id','trim|is_natural|required');
		if($this->form_validation->run() === FALSE)
		{
			$this->render('admin/categories/edit_pack_view');
		}
		else
		{
			$datetime = date('Y-m-d H:i:s');
			$pack_id = $this->input->post('pack_id');
			$parent_id = $this->input->post('parent_id');
			$name = $this->input->post('name');
			$order = $this->input->post('order');

			if($this->pack_model->update($pack_id, array('name' => $name, 'order' => $order,'parent_id' => $parent_id,'updated_at' => $datetime,'updated_by' => $this->data['current_user']->id)))
			{
				$this->session->set_flashdata('message','Category updated successfully');
			}
			redirect('admin/categories','refresh');
		}
	}


	public function delete($id_category)
	{
		if($this->category_model->delete($id_category) === FALSE)
		{
			$this->session->set_flashdata('message','Couldn\' delete category translation');
		}
		else
		{
			$this->session->set_flashdata('message','Category translation deleted successfully');
		}
		redirect('admin/categories','refresh');
	}
	public function delete_pack($id_pack)
	{
		if($this->pack_model->delete($id_pack) === FALSE)
		{
			$this->session->set_flashdata('message','Couldn\' delete category');
		}
		else
		{
			$this->session->set_flashdata('message','Category deleted successfully');
		}
		redirect('admin/categories','refresh');
	}
}

application/models/admin/Category_model.php

<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Category_model extends CI_Model
{

    public function __construct()
    {
        parent::__construct();
    }

    public function get_all($where = NULL)
    {
        if(isset($where))
        {
            $this->db->where($where);
        }
        $query = $this->db->get('categories');
        if($query->num_rows()>0)
        {
            return $query->result();
        }
        return FALSE;
    }



    public function get_by_id($category_id)
    {
        return $this->db->where('id',$category_id)->limit(1)->get('categories')->row();
    }

    public function create($data)
    {
        if($this->db->insert('categories',$data))
        {
            return $this->db->insert_id();
        }
        else
        {
            return FALSE;
        }
    }

    public function delete($category_id)
    {
        $this->db->delete('categories', array('id'=>$category_id));
        if($this->db->affected_rows()==0)
        {
            return FALSE;
        }
        return TRUE;
    }

}

application/models/admin/Pack_model.php

<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Pack_model extends CI_Model
{

    public function __construct()
    {
        parent::__construct();
    }

    public function get_all($where = NULL)
    {
        if(isset($where))
        {
            $this->db->where($where);
        }
        $this->db->order_by('language_name','ASC');
        $query = $this->db->get('languages');
        if($query->num_rows()>0)
        {
            return $query->result();
        }
        return FALSE;
    }

    public function get_by_id($pack_id)
    {
        return $this->db->where('id',$pack_id)->limit(1)->get('packs')->row();
    }

    public function get_all_categories_with_languages($where = NULL)
    {
        if(isset($where))
        {
            $this->db->where($where);
        }
        $this->db->select('categories.*');
        $this->db->select('packs.id as pack_id, packs.name as pack_name');
        $this->db->select('languages.slug as language_slug');
        $this->db->join('categories','packs.id = categories.pack_id','left');
        $this->db->join('languages','categories.language_id = languages.id','left');
        $this->db->order_by('packs.updated_at DESC, packs.created_at DESC');
        $this->db->order_by('language_slug');
        $query = $this->db->get('packs');
        if($query->num_rows()>0)
        {
            $categories = array();
            foreach($query->result() as $row)
            {
                if(!array_key_exists($row->pack_id, $categories)) {
                    $categories[$row->pack_id] = array('name' => $row->pack_name, 'languages' => array());
                }
                $categories[$row->pack_id]['languages'][$row->language_id] = array('language_slug'=>$row->language_slug,'translation_id'=>$row->id);

            }
            return $categories;
        }
        else
        {
            return FALSE;
        }
    }

    public function create($data)
    {
        if($this->db->insert('packs',$data))
        {
            return $this->db->insert_id();
        }
        else
        {
            return FALSE;
        }
    }

    public function delete($pack_id)
    {
        $this->db->delete('categories', array('pack_id'=>$pack_id));
        $this->db->delete('packs',array('id'=>$pack_id));
        if($this->db->affected_rows()==0)
        {
            return FALSE;
        }
        return TRUE;
    }

    public function update($pack_id, $data)
    {
        $this->db->where('id',$pack_id);
        return $this->db->update('packs',$data);
    }
}

application/views/admin/categories/index_view.php

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<div class="container" style="margin-top:60px;">
    <div class="row">
        <div class="col-lg-12">
            <a href="<?php echo site_url('admin/categories/create');?>" class="btn btn-primary">Add category</a>
        </div>
    </div>
    <div class="row">
        <div class="col-lg-12" style="margin-top: 10px;">
            <?php
            echo '<table class="table table-hover table-bordered table-condensed">';
            echo '<tr><td rowspan="2">ID</td><td rowspan="2">Category name</td></td><td colspan="'.sizeof($languages).'">Languages</td><td rowspan="2">Operations</td></tr>';
            echo '<tr>';
            foreach($languages as $language)
            {
                echo '<td>'.$language->slug.'</td>';
            }
            echo '</tr>';
            if(!empty($categories))
            {

                foreach($categories as $id => $categ)
                {
                    echo '<tr>';
                    echo '<td>'.$id.'</td><td>'.$categ['name'].'</td>';
                    foreach($languages as $language)
                    {
                        echo '<td>';
                        if(isset($categ['languages'][$language->id]))
                        {
                            echo anchor('admin/categories/edit/'.$categ['languages'][$language->id]['translation_id'], '<span class="glyphicon glyphicon-pencil"></span>');
                            echo ' ';
                            echo anchor('admin/categories/delete/'.$categ['languages'][$language->id]['translation_id'], '<span class="glyphicon glyphicon-remove"></span>');
                        }
                        else
                        {
                            echo anchor('admin/categories/create/'.$id.'/'.$language->id, '<span class="glyphicon glyphicon-plus"></span>');
                        }
                        echo '</td>';
                    }

                    echo '<td>'.anchor('admin/categories/edit-pack/'.$id,'<span class="glyphicon glyphicon-pencil"></span>').' '.anchor('admin/categories/delete-pack/'.$id,'<span class="glyphicon glyphicon-remove"></span>').'</td>';
                    echo '</tr>';
                }

            }
            echo '</table>';
            ?>
        </div>
    </div>
</div>

application/views/admin/categories/create_view.php

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<div class="container" style="margin-top:60px;">
    <div class="row">
        <div class="col-lg-4 col-lg-offset-4">
            <h1>Add category</h1>
            <?php echo form_open('',array('class'=>'form-horizontal'));?>
            <div class="form-group">
                <?php
                echo form_label('Language','language_id');
                echo form_dropdown('language_id',$languages,set_value('language_id',$default_language),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Parent category','parent_id');
                echo form_dropdown('parent_id',$categories,set_value('parent_id',$parent_id),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category name','name');
                echo form_error('name');
                echo form_input('name',set_value('name'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category description','description');
                echo form_error('description');
                echo form_input('description',set_value('description'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category slug','slug');
                echo form_error('slug');
                echo form_input('slug',set_value('slug'),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Order','order');
                echo form_error('order');
                echo form_input('order',set_value('order',$order),'class="form-control"');
                ?>
            </div>
            <?php echo form_hidden('pack_id',set_value('pack_id',$pack_id));?>
            <?php echo form_submit('submit', 'Add category', 'class="btn btn-primary btn-lg btn-block"');?>
            <?php echo anchor('/admin/categories', 'Cancel','class="btn btn-default btn-lg btn-block"');?>
            <?php echo form_close();?>
        </div>
    </div>
</div>

application/views/admin/categories/edit_view.php

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<div class="container" style="margin-top:60px;">
    <div class="row">
        <div class="col-lg-4 col-lg-offset-4">
            <h1>Edit category translation</h1>
            <?php echo form_open('',array('class'=>'form-horizontal'));?>
            <div class="form-group">
                <?php
                echo form_label('Language','language_id');
                echo form_dropdown('language_id',$languages,set_value('language_id',$default_language),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Parent category','parent_id');
                echo form_dropdown('parent_id',$categories,set_value('parent_id',$parent_id),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category name','name');
                echo form_error('name');
                echo form_input('name',set_value('name',$category->name),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category description','description');
                echo form_error('description');
                echo form_input('description',set_value('description',$category->description),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category slug','slug');
                echo form_error('slug');
                echo form_input('slug',set_value('slug',$category->slug),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Order','order');
                echo form_error('order');
                echo form_input('order',set_value('order',$order),'class="form-control"');
                ?>
            </div>
            <?php echo form_hidden('pack_id',set_value('pack_id',$category->pack_id));?>
            <?php echo form_submit('submit', 'Edit category translation', 'class="btn btn-primary btn-lg btn-block"');?>
            <?php echo anchor('/admin/categories', 'Cancel','class="btn btn-default btn-lg btn-block"');?>
            <?php echo form_close();?>
        </div>
    </div>
</div>

application/views/admin/categories/edit_pack_view.php

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<div class="container" style="margin-top:60px;">
    <div class="row">
        <div class="col-lg-4 col-lg-offset-4">
            <h1>Edit category</h1>
            <?php echo form_open('',array('class'=>'form-horizontal'));?>
            <div class="form-group">
                <?php
                echo form_label('Parent category','parent_id');
                echo form_dropdown('parent_id',$categories,set_value('parent_id',$parent_id),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Category name','name');
                echo form_error('name');
                echo form_input('name',set_value('name',$pack->name),'class="form-control"');
                ?>
            </div>
            <div class="form-group">
                <?php
                echo form_label('Order','order');
                echo form_error('order');
                echo form_input('order',set_value('order',$order),'class="form-control"');
                ?>
            </div>
            <?php echo form_hidden('pack_id',set_value('pack_id',$pack->id));?>
            <?php echo form_submit('submit', 'Edit category', 'class="btn btn-primary btn-lg btn-block"');?>
            <?php echo anchor('/admin/categories', 'Cancel','class="btn btn-default btn-lg btn-block"');?>
            <?php echo form_close();?>
        </div>
    </div>
</div>

Well… As you can see, creating a multilanguage site is not that easy as it may seem…

15 Comments

  1. Noticed you merged the category_translation and the page_translation. Why were they separate in the first place.I was wondering what the categories were. Great tutorial. I was searching on how to use Ion_auth. This opened my eyes to multi_language applications which is great. Nice one

    • Are you referring to one of my Github repositories (https://github.com/avenirer/CodeIgniter-multilanguage-site)? If you are, then congratulations for looking into it 🙂 What I thought in the first place when creating the multilanguage site was to have different tables for different content types, thinking that… well… they are different. But a week ago I thought: “Wait a minute… They are not that different… So why not create a ‘contents’ table and a ‘content_translations’ table?”.

      PS: If you want to try the site on Github, you can use “administrator” as username and “password” as password.

    • Now you can find the sql file in the github repository. About working on some projects together, why not?

        • This is a tutorial, not an application for you to test… You are doing the testing by creating the categories, editing them, deleting them and listing them.

      • Hi Avenir ,

        Why this site wont work when i try to load from localhost ? It shows to me a lot of errors and when i try to login it just refreshes the page , and when i check in database there is no username and password registered as you have mentioned . I downloaded the code from your git repository but it doesn’t work for me . Can you help me ?

        Thanks !

        • It seems that the problem comes from the sql file. Try downloading again from Github and use multilanguage.sql to set up the tables

Leave a Reply

Your email address will not be published. Required fields are marked *