(created at: March 31, 2015; last update: December 08, 2015)
Now that we’ve created a page, we want to see all pages in a list. So let’s create an index() method in our Pages controller.
What we want is the pages with translations, we also should limit the results so that the list would not be too long. So why not limit the results to 30 “pages”. How can we do that. Fortunately, MY_Model (https://github.com/avenirer/CodeIgniter-MY_Model) has helper functions that will make our work easier. Also, it will help us by giving us the translations for the pages.
So how do we do this? We first retrieve the pages:
$total_pages = $this->page_model->count(); $pages = $this->page_model->paginate(30,$total_pages);
Now, we can verify that we’ve retrieved the pages:
echo '<pre>'; print_r($pages); echo '</pre>';
Well… it’s only one page… But where is the translation? To retrieve the translations of the pages we first need to establish a relationship between our models.
Let’s see… A page has many translations, and a translation has one page. Right?
How do we set these relationships? We define them in the models that extended the MY_Model. According to the documentation, we have to define the $has_many property.
We first start with the Page_model model:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Page_model extends MY_Model { public function __construct() { $this->has_many['translations'] = array('Page_translation_model','page_id','id'); 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'=>''), 'slug' => array('field'=>'slug', 'label'=>'Slug', 'rules'=>'trim'), 'order' => array('field'=>'order','label'=>'Order','rules'=>'trim|is_natural|required'), 'teaser' => array('field'=>'teaser','label'=>'Teaser','rules'=>'trim'), 'content' => array('field'=>'content','label'=>'Content','rules'=>'trim'), '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'), 'page_id' => array('field'=>'page_id', 'label'=>'Page ID', 'rules'=>'trim|is_natural|required'), 'language_slug' => array('field'=>'language_slug','label'=>'language_slug','rules'=>'trim|required') ), 'update' => array() ); }
See line 9? That’s it. We defined a “has many” relationship. For what we need, that is all. But let’s also define the one has one relationship inside our Page_translation_model model. For this we need to set the $has_one property:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Page_translation_model extends MY_Model { public function __construct() { $this->has_one['page'] = array('Page_model','id','page_id'); parent::__construct(); } }
…and that’s it. As you can see, in both cases we’ve passed to the property an array which has as elements: the model name, the foreign key and the local relationship key.
Now returning to our controller we only have to add a new method in the method chaining. So, instead of writing: $pages = $this->page_model->paginate(30,$total_pages); , we write: $pages = $this->page_model->with(‘translations’)->paginate(30,$total_pages);
Now, how do we order the list? How about using the created_at and modified_at fields to order the list descending?
After this we only need to pass the pages to our view. But before we do that, let’s clean it a bit and make sure it is a list and not only one row (or else we would get a big error…):
$list_pages = array(); if($pages = $this->page_model->order_by('created_at, updated_at','desc')->with('translations')->paginate(30,$total_pages)) { if(sizeof($pages)==1) { $list_pages[$pages->id] = array('created_at' => $pages->created_at, 'last_update' => $pages->updated_at, 'deleted' => $pages->deleted_at, 'translations' => array()); if(isset($pages->translations)) { foreach ($pages->translations as $translation) { $list_pages[$pages->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[$pages->id]['title'] = $translation->title; } elseif(strlen($list_pages[$pages->id]['title'])==0) { $list_pages[$pages->id]['title'] = $translation->title; } } } } else { foreach ($pages as $page) { $list_pages[$page->id] = array('created_at' => $page->created_at, 'last_update' => $page->updated_at, 'deleted' => $page->deleted_at, 'translations' => array(), 'title'=>''); if(isset($page->translations)) { foreach ($page->translations as $translation) { $list_pages[$page->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[$page->id]['title'] = $translation->title; } elseif(strlen($list_pages[$page->id]['title'])==0) { $list_pages[$page->id]['title'] = $translation->title; } } } } } }
Now we send the list of pages to the view: $this->data[‘pages’] = $list_pages;
Also, we need the links to the next and previous list of 30 pages. We use our MY_Model helper for this: $this->data[‘next_previous_pages’] = $this->page_model->all_pages;
And we render the view: $this->render(‘admin/pages/index_view’);
Now, we create the index_view.php inside views/admin/pages directory:
<?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"> <!-- Single button --> <div class="btn-group"> <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">Add page <span class="caret"></span> </button> <ul class="dropdown-menu" role="menu"> <?php foreach($langs as $slug => $language) { echo '<li>'.anchor('admin/pages/create/'.$slug,$language['name']).'</li>'; } ?> </ul> </div> </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>'; echo '<td rowspan="2">ID</td>'; echo '<td rowspan="2">Page title</td>'; echo '<td colspan="'.sizeof($langs).'">Translations</td>'; echo '<td rowspan="2">Created at</td>'; echo '<td rowspan="2">Last update</td>'; echo '<td rowspan="2">Operations</td>'; echo '</tr>'; echo '<tr>'; foreach($langs as $slug => $language) { echo '<td>'.$slug.'</td>'; } echo '</tr>'; if(!empty($pages)) { foreach($pages as $page_id => $page) { echo '<tr>'; echo '<td>'.$page_id.'</td><td>'.$page['title'].'</td>'; foreach($langs as $slug=>$language) { echo '<td>'; if(array_key_exists($slug,$page['translations'])) { echo anchor('admin/pages/edit/'.$slug.'/'.$page_id,'<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>'); echo ' '.anchor('admin/pages/delete/'.$slug.'/'.$page_id,'<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>'); } else { echo anchor('admin/pages/create/'.$slug.'/'.$page_id,'<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>'); } echo '</td>'; } echo '<td>'.$page['created_at'].'</td>'; echo '<td>'.$page['last_update'].'</td>'; echo '<td>'.anchor('admin/pages/delete/all/'.$page_id,'<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>').'</td>'; echo '</tr>'; } } echo '</table>'; echo '<div>'.$next_previous_pages.'</div>'; ?> </div> </div> </div>
Cool. Let’s see the Pages controller again:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Pages 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 Pages page'); redirect('admin','refresh'); } $this->load->model('page_model'); $this->load->model('page_translation_model'); $this->load->model('language_model'); $this->load->library('form_validation'); $this->load->helper('text'); } public function index() { $total_pages = $this->page_model->count(); $list_pages = array(); if($pages = $this->page_model->order_by('created_at, updated_at','desc')->with('translations')->paginate(30,$total_pages)) { if(sizeof($pages)==1) { $list_pages[$pages->id] = array('created_at' => $pages->created_at, 'last_update' => $pages->updated_at, 'deleted' => $pages->deleted_at, 'translations' => array()); if(isset($pages->translations)) { foreach ($pages->translations as $translation) { $list_pages[$pages->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[$pages->id]['title'] = $translation->title; } elseif(strlen($list_pages[$pages->id]['title'])==0) { $list_pages[$pages->id]['title'] = $translation->title; } } } } else { foreach ($pages as $page) { $list_pages[$page->id] = array('created_at' => $page->created_at, 'last_update' => $page->updated_at, 'deleted' => $page->deleted_at, 'translations' => array(), 'title'=>''); if(isset($page->translations)) { foreach ($page->translations as $translation) { $list_pages[$page->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[$page->id]['title'] = $translation->title; } elseif(strlen($list_pages[$page->id]['title'])==0) { $list_pages[$page->id]['title'] = $translation->title; } } } } } } $this->data['pages'] = $list_pages; $this->data['next_previous_pages'] = $this->page_model->all_pages; $this->render('admin/pages/index_view'); } public function create($language_slug = NULL, $page_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]['language_directory']; $this->data['language_slug'] = $language_slug; if($page_id != 0 && $this->page_model->get($page_id)===FALSE) { $page_id = 0; } if($this->page_translation_model->where(array('page_id'=>$page_id,'language_slug'=>$language_slug))->get()) { $this->session->set_flashdata('message', 'A translation for that page already exists.'); redirect('admin/pages', 'refresh'); } $this->data['page_id'] = $page_id; $pages = $this->page_translation_model->where('language_slug',$language_slug)->order_by('menu_title')->fields('id,menu_title')->get_all(); $this->data['parent_pages'] = array('0'=>'No parent page'); if(!empty($pages)) { foreach($pages as $page) { $this->data['parent_pages'][$page->id] = $page->menu_title; } } $rules = $this->page_model->rules; $this->form_validation->set_rules($rules['insert']); if($this->form_validation->run()===FALSE) { $this->render('admin/pages/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'); $content = $this->input->post('content'); $teaser = (strlen($this->input->post('teaser')) > 0) ? $this->input->post('teaser') : substr($content, 0, strpos($content, '<!--more-->')); $page_title = (strlen($this->input->post('page_title')) > 0) ? $this->input->post('page_title') : $title; $page_description = (strlen($this->input->post('page_description')) > 0) ? $this->input->post('page_description') : ellipsize($teaser, 160); $page_keywords = $this->input->post('page_keywords'); $page_id = $this->input->post('page_id'); $language_slug = $this->input->post('language_slug'); if ($page_id == 0) { $page_id = $this->page_model->insert(array('parent_id' => $parent_id, 'order' => $order, 'created_by'=>$this->user_id)); } $insert_data = array('page_id'=>$page_id,'title' => $title, 'menu_title' => $menu_title, 'teaser' => $teaser,'content' => $content,'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->page_translation_model->insert($insert_data)) { $url = $this->_verify_slug($slug,$language_slug); $this->slug_model->insert(array( 'content_type'=>'page', 'content_id'=>$page_id, 'translation_id'=>$translation_id, 'language_slug'=>$language_slug, 'url'=>$url, 'created_by'=>$this->user_id)); } redirect('admin/pages','refresh'); } } private function _verify_slug($str,$language) { $this->load->model('slug_model'); 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; } }
Thank you for such a great tutorial. Although is its is intended towards beginners, but it has helped me a web site in two languages. Initially i was creating different models controllers for each language, but your approach for routing is really great… Thanks a lot..!! Keep Sharing for guys like US..!! 🙂
I would also like to mention an error in index() function of Pages controller. After the
“if(sizeof($pages)==1) {}” condition, $pages->anyobject is not accessed directly as $pages is an array. So you’ve to change $pages to $pages[0] inside this single page condition.
Thank you for your input. I sure hope you were paid well for creating a multilanguage site using my ideas. Did you know you can buy me a coffee? Regarding the error, the sizeof() only verifies if there was only one page returned. I will take a look again. Thank you.
hello,
firstly, thank your for your tutorial.
it’s such a huge help for me.
i’ve followed your tutorial until the previous part and i have no problem so far.
but, right now i’m having some problems in this part.
i’ve followed this part from top to bottom and when i try to access the page.
the url is : localhost/blog/admin/pages.
i’ve got several errors, those are
“Severity: Notice
Message: Undefined index: name
Filename: pages/index_view.php
Line Number: 13”
on “Add Page” button
line number 13 is : echo ”.anchor(‘admin/pages/create/’.$slug,$language[‘name’]).”;
and then i tried to directly access the “create” page with the url “/admin/pages/create/en/1”
it also gave me an error.
here it is
“Message: Undefined index: name
Filename: admin/Pages.php
Line Number: 78”
Line number 78 is : $this->data[‘content_language’] = $this->langs[$language_slug][‘name’];
thanks beforehand.
You are right. While creating this series I was working and changing the files. But it seems I forgot to change the episodes. You should change “name” with “language_directory”. I will also update this episode. Thank you for noticing. If you find any more errors, please, do tell me.
i love you work, your are a great man 🙂
there is something i don’t know if it’s not working for me only but shouldn’t it be
if (sizeof($pages) == 1) {
$list_pages[$pages[0]->id] = array(‘created_at’ => $pages[0]->created_at, ‘last_update’ => $pages[0]->updated_at, ‘deleted’ => $pages[0]->deleted_at, ‘translations’ => array());
if (isset($pages[0]->translations)) {
foreach ($pages[0]->translations as $translation) {
$list_pages[$pages[0]->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[$pages[0]->id][‘title’] = $translation->title;
} elseif (strlen($list_pages[$pages[0]->id][‘title’]) == 0) {
$list_pages[$pages[0]->id][‘title’] = $translation->title;
}
}
}
}
please update it 🙂
You may be right. But that depends on the MY_Model. At the time when I wrote the tutorial my MY_Model looked differently. Thank you for repairing.