CodeIgniter Forums
Codeigniter 3 micro-blogging application architecture flaws - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Using CodeIgniter (https://forum.codeigniter.com/forum-5.html)
+--- Forum: Best Practices (https://forum.codeigniter.com/forum-12.html)
+--- Thread: Codeigniter 3 micro-blogging application architecture flaws (/thread-73836.html)



Codeigniter 3 micro-blogging application architecture flaws - Ajax30 - 06-10-2019

I am working on a basic blog application in Codeigniter 3.1.8 and Bootstrap 4.
The application allows Registration (as an author), Login, adding Categories and Posts.
I have created an installation process for the application: after creating a database and providing its credentials to the 
Code:
application/config/database.php

 file, you can run the 

Code:
Install

 controller which will create all the necessary tables:

Code:
class Install extends CI_Controller {
   public function __construct()
   {
       parent::__construct();
   }

   public function index(){
       // Create all the database tables if there are none
       // by redirecting to the Migrations controller
       $tables = $this->db->list_tables();
       if (count($tables) == 0) {
           redirect('migrate');
       } else {
           redirect('/');
       }
   }
}

After that, you can register as an author. Being the first registered author, you are also an admin, meaning that your author account does not require activation (and the value for the 
Code:
is_admin

 column has a value of 

Code:
1

 in the database record for you).

All the future authors will need their accounts activated by you in order to publish articles (posts).
It is a rather ambitious project, although I did not start it with great plans in mind. I just wanted to learn Codeigniter 3. You can see it on my GitHub account.
There are a few possible issues:
  1. The application does not use HMVC, but "classic" MVC.

  2. It does not have a clear separation between its front and its back (admin)

  3. I want to add a theming feature and I kinda got stuck with it; I don't know what approach to use.

  4. My controllers may be a over-coded.
Here is the 
Code:
Posts

 controller:

Code:
class Posts extends CI_Controller {

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

   private function _initPagination($path, $totalRows, $query_string_segment = 'page') {
   //load and configure pagination
       $this->load->library('pagination');
       $config['base_url'] = base_url($path);
       $config['query_string_segment'] = $query_string_segment;
       $config['enable_query_strings'] =TRUE;
       $config['reuse_query_string'] =TRUE;
       $config['total_rows'] = $totalRows;
       $config['per_page'] = 12;
       if (!isset($_GET[$config['query_string_segment']]) || $_GET[$config['query_string_segment']] < 1) {
           $_GET[$config['query_string_segment']] = 1;
       }
       $this->pagination->initialize($config);

       $limit = $config['per_page'];
       $offset = ($this->input->get($config['query_string_segment']) - 1) * $limit;

       return ['limit' => $limit, 'offset' => $offset];
   }

   public function index() {

   //call initialization method
       $config = $this->_initPagination("/", $this->Posts_model->get_num_rows());

       $data = $this->Static_model->get_static_data();
       $data['pages'] = $this->Pages_model->get_pages();
       $data['categories'] = $this->Categories_model->get_categories();  

       //use limit and offset returned by _initPaginator method
       $data['posts'] = $this->Posts_model->get_posts($config['limit'], $config['offset']);
       $this->load->view('partials/header', $data);
       $this->load->view('posts');
       $this->load->view('partials/footer');
   }

   public function search() {
   // Force validation since the form's method is GET
       $this->form_validation->set_data($this->input->get());
       $this->form_validation->set_rules('search', 'Search term', 'required|trim|min_length[3]',array('min_length' => 'The Search term must be at least 3 characters long.'));
       $this->form_validation->set_error_delimiters('<p class = "error search-error">', '</p>
           ');
       // If search fails
       if ($this->form_validation->run() === FALSE) {
           return $this->index();
       } else {
           $expression = $this->input->get('search');
           $posts_count = $this->Posts_model->search_count($expression);
           $query_string_segment = 'page';
           $config = $this->_initPagination("/posts/search", $posts_count, $query_string_segment);
           $data = $this->Static_model->get_static_data();
           $data['pages'] = $this->Pages_model->get_pages();
           $data['categories'] = $this->Categories_model->get_categories();
     //use limit and offset returned by _initPaginator method
           $data['posts'] = $this->Posts_model->search($expression, $config['limit'], $config['offset']);
           $data['expression'] = $expression;
           $data['posts_count'] = $posts_count;
           $this->load->view('partials/header', $data);
           $this->load->view('search');
           $this->load->view('partials/footer');
       }
   }

   public function byauthor($authorid){
       $data = $this->Static_model->get_static_data();
       $data['pages'] = $this->Pages_model->get_pages();
       $data['categories'] = $this->Categories_model->get_categories();
       $data['posts'] = $this->Posts_model->get_posts_by_author($authorid);
       $data['posts_count'] = $this->Posts_model->posts_by_author_count($authorid);
       $data['posts_author'] = $this->Posts_model->posts_author($authorid);

       $this->load->view('partials/header', $data);
       $this->load->view('posts_by_author');
       $this->load->view('partials/footer');
   }

   public function post($slug) {
       $data = $this->Static_model->get_static_data();
       $data['pages'] = $this->Pages_model->get_pages();
       $data['categories'] = $this->Categories_model->get_categories();
       $data['posts'] = $this->Posts_model->sidebar_posts($limit=5, $offset=0);
       $data['post'] = $this->Posts_model->get_post($slug);

       if ($data['categories']) {
           foreach ($data['categories'] as &$category) {
               $category->posts_count = $this->Posts_model->count_posts_in_category($category->id);
           }
       }

       if (!empty($data['post'])) {
           // Overwrite the default tagline with the post title
           $data['tagline'] = $data['post']->title;

           // Get post comments
           $post_id = $data['post']->id;
           $data['comments'] = $this->Comments_model->get_comments($post_id);

           $this->load->view('partials/header', $data);
           $this->load->view('post');
       } else {
           $data['tagline'] = "Page not found";
           $this->load->view('partials/header', $data);
           $this->load->view('404');
       }
       $this->load->view('partials/footer');
   }

   public function create() {

       // Only logged in users can create posts
       if (!$this->session->userdata('is_logged_in')) {
           redirect('login');
       }

       $data = $this->Static_model->get_static_data();
       $data['pages'] = $this->Pages_model->get_pages();
       $data['tagline'] = "Add New Post";
       $data['categories'] = $this->Categories_model->get_categories();
       $data['posts'] = $this->Posts_model->sidebar_posts($limit=5, $offset=0);

       if ($data['categories']) {
           foreach ($data['categories'] as &$category) {
               $category->posts_count = $this->Posts_model->count_posts_in_category($category->id);
           }
       }

       $this->form_validation->set_rules('title', 'Title', 'required');
       $this->form_validation->set_rules('desc', 'Short description', 'required');
       $this->form_validation->set_rules('body', 'Body', 'required');
       $this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');

       if($this->form_validation->run() === FALSE){
           $this->load->view('partials/header', $data);
           $this->load->view('create-post');
           $this->load->view('partials/footer');
       } else {
           // Create slug (from title)
           $slug = url_title($this->input->post('title'), 'dash', TRUE);
           $slugcount = $this->Posts_model->slug_count($slug);
           if ($slugcount > 0) {
               $slug = $slug."-".$slugcount;
           }

           // Upload image
           $config['upload_path'] = './assets/img/posts';
           $config['allowed_types'] = 'jpg|png';
           $config['max_size'] = '2048';

           $this->load->library('upload', $config);

           if(!$this->upload->do_upload()){
               $errors = array('error' => $this->upload->display_errors());
               $post_image = 'default.jpg';
           } else {
               $data = array('upload_data' => $this->upload->data());
               $post_image = $_FILES['userfile']['name'];
           }

           $this->Posts_model->create_post($post_image, $slug);
           $this->session->set_flashdata('post_created', 'Your post has been created');
           redirect('/');
       }
   }

   public function edit($id) {
       // Only logged in users can edit posts
       if (!$this->session->userdata('is_logged_in')) {
           redirect('login');
       }

       $data = $this->Static_model->get_static_data();
       $data['pages'] = $this->Pages_model->get_pages();
       $data['categories'] = $this->Categories_model->get_categories();
       $data['posts'] = $this->Posts_model->sidebar_posts($limit=5, $offset=0);
       $data['post'] = $this->Posts_model->get_post($id);
       if ($this->session->userdata('user_id') == $data['post']->author_id) {
           $data['tagline'] = 'Edit the post "' . $data['post']->title . '"';
           $this->load->view('partials/header', $data);
           $this->load->view('edit-post');
           $this->load->view('partials/footer');
       } else {
           /* If the current user is not the author
           of the post do not alow edit */
           redirect('/' . $id);
       }
   }

   public function update() {
       // Form data validation rules
       $this->form_validation->set_rules('title', 'Title', 'required',  array('required' => 'The %s field can not be empty'));
       $this->form_validation->set_rules('desc', 'Short description', 'required',  array('required' => 'The %s field can not be empty'));
       $this->form_validation->set_rules('body', 'Body', 'required',  array('required' => 'The %s field can not be empty'));
       $this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');

       $id = $this->input->post('id');

       // Update slug (from title)
       if (!empty($this->input->post('title'))) {
           $slug = url_title($this->input->post('title'), 'dash', TRUE);
           $slugcount = $this->Posts_model->slug_count($slug);
           if ($slugcount > 0) {
               $slug = $slug."-".$slugcount;
           }
       } else {
           $slug = $this->input->post('slug');
       }

   // Upload image
       $config['upload_path'] = './assets/img/posts';
       $config['allowed_types'] = 'jpg|png';
       $config['max_size'] = '2048';

       $this->load->library('upload', $config);

       if ( isset($_FILES['userfile']['name']) && $_FILES['userfile']['name'] != null )
       {
           // Use name field in do_upload method
           if (!$this->upload->do_upload('userfile')) {
               $errors = array('error' => $this->upload->display_errors());

           } else {
               $data = $this->upload->data();
               $post_image = $data[ 'raw_name'].$data[ 'file_ext'];
           }
       }
       else {
           $post_image = $this->input->post('postimage');
       }


       if ($this->form_validation->run()) {
           $this->Posts_model->update_post($id, $post_image, $slug);
           $this->session->set_flashdata('post_updated', 'Your post has been updated');
           redirect('/' . $slug);
       } else {
           $this->form_validation->run();
           $this->session->set_flashdata('errors', validation_errors());
           redirect('/posts/edit/' . $slug);
       }
   }

   public function delete($slug) {
       // Only logged in users can delete posts
       if (!$this->session->userdata('is_logged_in')) {
           redirect('login');
       }

       $data['post'] = $this->Posts_model->get_post($slug);
       if ($this->session->userdata('user_id') == $data['post']->author_id)
     {
           $this->Posts_model->delete_post($slug);
           $this->session->set_flashdata('post_deleted', 'The post has been deleted');
           redirect('/');
       } else {
           /* If the current user is not the author
           of the post do not alow delete */
           redirect('/' . $slug);
       }
     }    
   }

Please help me with useful feedback and suggestions.


RE: Codeigniter 3 micro-blogging application architecture flaws - php_rocs - 06-11-2019

@Ajax30,

What is your overall objective with building this tool? What is it that you want this tool to accomplish? Is the focus Cloud based or regular hosting (do you want to support both)? Your request is VERY open ended. There are many suggestions or ideas that could be given. There are many videos and other CI projects that could definitely give you guidance moving forward.


RE: Codeigniter 3 micro-blogging application architecture flaws - Ajax30 - 06-11-2019

This application is for shared hosting. I have not been able to find a tutorial on how to introduce themming (similar to what Wordpress has). Find explicit quesions here.


RE: Codeigniter 3 micro-blogging application architecture flaws - website - 06-11-2019

Maybe if you want multilevel users, try Ion Auth, or Luthier CI=>SimpleAuth.