10 Replies - 609 Views - Last Post: 20 November 2015 - 10:34 AM Rate Topic: -----

#1 chris98   User is offline

  • D.I.C Lover

Reputation: 41
  • View blog
  • Posts: 1,112
  • Joined: 06-July 13

MVC Forum Submission help

Posted 15 November 2015 - 09:34 AM

I've adapted this code a lot for my site and I want it to run MVC.

It's very easy to just render templates statically, but when it comes to actually submitting forms I'm at a real dead-end. I want this to be done right - but I'm not sure how this should be done.

My instinct is to have it like the code below, but I'm just not happy with this. For one, the method seems far too long, it seems too complex and I don't really like having to access POST (or GET) things directly. I'd rather somehow have this in some kind of external file but I don't know how to do this.

How could I properly have a form submission that will be handled correctly?

<?php
class contact_controller extends base_controller
{
	public function __construct($registry)
	{
		$this->registry = $registry;
		$this->user = $registry->user;
		$this->config = $registry->config;
		$this->db = $registry->db;
		$this->template = $registry->template;
		$this->cache = $registry->cache;
	}

	public function index_action()
	{ 
		$departments = array();
		$ps = $this->db->select('departments', 'department AS name, id', array(), 'allow_submissions=1', 'position ASC');
		foreach ($ps as $cur_department)
			$departments[] = array('id' => $cur_department['id'], 'title' => $cur_department['name']);

		$this->template->header = array(
			'title' => 'Contact Us',
			'keywords' => '...',
			'description' => '......',
		);

		$this->template->data = array(
			'csrf_token' => generate_csrf_token('contact.php'),
			'POST' => $_POST,
			'departments' => $departments,
			'errors' => (isset($this->errors) ? $this->errors : array()),
		);

		$this->template->render('contact');
	}

	public function post_action()
	{
		if ($this->user['is_bot'])
			new error('403');

		if (isset($_POST['form_sent']))
		{
			$this->errors = array();
			confirm_referrer('contact.php');

			$hash = random_pass(12);
			$name = isset($_POST['name']) ? panther_trim($_POST['name']) : '';
			$email = isset($_POST['email']) ? panther_trim($_POST['email']) : '';
			$subject = isset($_POST['subject']) ? panther_trim($_POST['subject']) : '';
			$message = isset($_POST['message']) ? panther_trim($_POST['message']) : '';
			$department = isset($_POST['department']) ? panther_trim($_POST['department']) : '';

			require PANTHER_ROOT.'lang/English/post.php';

			if (strlen($name) < 5)
				$this->errors[] = 'You must enter a name bigger than five characters';

			if (strlen($message) > PANTHER_MAX_POSTSIZE)
				$this->errors[] = sprintf($lang_post['Too long message'], forum_number_format(PANTHER_MAX_POSTSIZE));
			else if ($this->config['p_message_all_caps'] == '0' && is_all_uppercase($message) && !$this->user['is_admmod'])
				$this->errors[] = $lang_post['All caps message'];

			if ($message == '')
				$this->errors[] = $lang_post['No message'];
			else if ($this->config['o_censoring'] == '1')
			{
				// Censor message to see if that causes problems
				$censored_message = panther_trim(censor_words($message));

				if ($censored_message == '')
					$this->errors[] = $lang_post['No message after censoring'];
			}
		
			if ($this->config['o_censoring'] == '1')
				$censored_subject = panther_trim(censor_words($subject));

			if ($subject == '')
				$this->errors[] = $lang_post['No subject'];
			else if ($this->config['o_censoring'] == '1' && $censored_subject == '')
				$this->errors[] = $lang_post['No subject after censoring'];
			else if (panther_strlen($subject) > 70)
				$this->errors[] = $lang_post['Too long subject'];
			else if ($this->config['p_subject_all_caps'] == '0' && is_all_uppercase($subject) && !$this->user['is_admmod'])
				$this->errors[] = $lang_post['All caps subject'];

			//require PANTHER_ROOT.'include/email.php';

			//if (!$mailer->is_valid_email($email))
				//$this->errors[] = 'Please enter a valid email.';

			//if (!in_array($department, $ids))
				//$this->errors[] = 'You must select a valid department';

			$data = array(
				':time' => time(),
				':ip' => get_remote_address(),
			);

			$ps = $this->db->select('replies', 1, $data, 'remote_address=:ip AND posted > (:time-3600)');
			if ($ps->rowCount())
				$this->errors[] = 'Another ticket (or reply) has been made by this IP address within the last hour. To prevent automated spam, at least one hour has to pass between new tickets from the same IP address.';

			if (empty($this->errors))
			{
				$ps = $this->db->select('statuses', 'id', array(), 'default_status=1', 'id ASC LIMIT 1');
				$status = $ps->fetchColumn();
		
				$ps = $this->db->select('priorities', 'id', array(), '`default`=1', 'id ASC LIMIT 1');
				$priority = $ps->fetchColumn();
		
				$insert = array(
					'name' => $name,
					'email' => $email,
					'subject' => $subject,
					'department' => $department,
					'status' => $status,
					'priority' => $priority,
					'hash' => $hash,
				);
		
				$this->db->insert('tickets', $insert);
				$id = $this->db->lastInsertId('tickets');
		
				$insert = array(
					'message' => $message,
					'remote_address' => get_remote_address(),
					'posted' => time(),
					'ticket_id' => $id,
				);
				
				$this->db->insert('replies', $insert);
				$last_reply = $this->db->lastInsertId('replies');
		
				$update = array(
					'last_reply' => $last_reply,
				);
		
				$data = array(
					':id' => $id,
				);
		
				$this->db->update('tickets', $update, 'id=:id', $data);
		
				$info = array(
					'subject' => array(
						'<id>' => $id,
					),
					'message' => array(
						'<id>' => $id,
						'<ticket_link>' => 'ticket.php?id='.$id.'&hash='.$hash,
						'<subject>' => $subject,
						'<ticket_name>' => $name,
						'<message>' => $mailer->bbcode2email($message),
						'<department>' => strtolower($names[$department]),
					)
				);
	
				$mail_tpl = $mailer->parse(mail_template('contact_email'), $info);
				$mailer->send($email, $mail_tpl['subject'], $mail_tpl['message']);
	
				echo 'Thank you for contacting support!';
			}
		}

		$this->index_action();
	}
}


This post has been edited by chris98: 15 November 2015 - 09:36 AM


Is This A Good Question/Topic? 0
  • +

Replies To: MVC Forum Submission help

#2 ArtificialSoldier   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2465
  • View blog
  • Posts: 7,499
  • Joined: 15-January 14

Re: MVC Forum Submission help

Posted 16 November 2015 - 10:19 AM

Quote

I don't really like having to access POST (or GET) things directly

How else would you prefer to access submitted data?
Was This Post Helpful? 0
  • +
  • -

#3 chris98   User is offline

  • D.I.C Lover

Reputation: 41
  • View blog
  • Posts: 1,112
  • Joined: 06-July 13

Re: MVC Forum Submission help

Posted 16 November 2015 - 10:55 AM

I'm not sure. Some other kind of method that grabs what I need is probably best but I'm not sure where to put it. The router maybe?

Back to the original question though, is that what should be done, what I have up there? I feel I'm chucking in code into the controller which shouldn't be there, but I don't know how to get it somewhere else (e.g. in a different class included) because I'm not sure how to get the registry there without directly adding it through the __construct - such as $class = new class($this->registry).

What I'm saying is I feel like the controller should be for just direct actions that process and then send it to the view (in my case Twig).

This post has been edited by chris98: 16 November 2015 - 10:58 AM

Was This Post Helpful? 0
  • +
  • -

#4 ArtificialSoldier   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2465
  • View blog
  • Posts: 7,499
  • Joined: 15-January 14

Re: MVC Forum Submission help

Posted 16 November 2015 - 11:24 AM

If the controller doesn't handle an action like a form submission, do you think that would go in the model or view?
Was This Post Helpful? 0
  • +
  • -

#5 chris98   User is offline

  • D.I.C Lover

Reputation: 41
  • View blog
  • Posts: 1,112
  • Joined: 06-July 13

Re: MVC Forum Submission help

Posted 16 November 2015 - 12:43 PM

I know it shouldn't go in the view, which only leaves the model. But that's just my problem. I don't know how to handle this cleanly.

I could do this now in the controller or some easy way, but it would completely mess my code up which is exactly what I don't want. I'd really like to have this done properly.

Should I create a separate action in the controller and link to some kind of class? Should I use in if (isset($_POST['form_sent'])) to check in the original method and then create a class instance which does processing?

These are but just a few questions I'm really struggling to find an answer to, on top of the original one of including classes with access to the registry, without actually passing the registry itself.

That example I adapted my code from is massively outdated, and although I've replaced things such as the __autoload method, it still looks too heavy from the controller's point of view and I really don't know how to change this.

This post has been edited by chris98: 16 November 2015 - 12:47 PM

Was This Post Helpful? 0
  • +
  • -

#6 Atli   User is offline

  • Enhance Your Calm
  • member icon

Reputation: 4241
  • View blog
  • Posts: 7,216
  • Joined: 08-June 10

Re: MVC Forum Submission help

Posted 16 November 2015 - 12:50 PM

In my opinion you are doing a little bit too much DB interaction in the controller. That sort of thing should belong to the Model layer, really. - You've got a CRUD DB interface in there, it seems, but still you are essentially creating whole SQL statements in the controller code, complete with conditions and sort orders:
$ps = $this->db->select('statuses', 'id', array(), 'default_status=1', 'id ASC LIMIT 1');


Ideally you'd be doing something akin to:
$dbm = new StatusModel();
$ps = $dbm->getDefaultStatus();


And then the model code would deal with all the details of that DB interaction.

This way it can be very easily re-used and tested. This is obviously a rather trivial example, but in general this makes a huge difference in large systems.


As for the request data, it's not overly difficult to create (or even just re-use) an OOP interface for it. For example, I rather like how Symfony does it, using dependency-injection in the action method signature to pass a Request object, complete with all the passed data:
<?php
namespace FooBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class BarController extends Controller
{
    public function indexAction(Request $request)
    {
        $post = $request->request;
        if ($post->has("message")) {
            $twitterApi = $this->container->get("twitter.api");
            $success = $twitterApi->tweet($post->get("message"));

            return $this->render("resultview.html.twig", ["success" => $success]);
        } else {
            throw new BadRequestHttpException("Username is required.");
        }
    }
}



Also note how it uses the $this->container object to access services outside the controller. It's not immediately creating references to all of them in the controller constructor, like you do with your $reqistry object, but rather making them accessible through it, creating them on-demand.


Obviously Symfony's bootstrapper is a little more advanced than most people would try to build themselves, but it's not altogether difficult to replicate these concepts in a simpler form, fitting your own framework.
Was This Post Helpful? 0
  • +
  • -

#7 chris98   User is offline

  • D.I.C Lover

Reputation: 41
  • View blog
  • Posts: 1,112
  • Joined: 06-July 13

Re: MVC Forum Submission help

Posted 17 November 2015 - 04:37 AM

View PostAtli, on 16 November 2015 - 07:50 PM, said:

Ideally you'd be doing something akin to:
$dbm = new StatusModel();
$ps = $dbm->getDefaultStatus();


And then the model code would deal with all the details of that DB interaction.


That code is generally what I'd like to see - but that's also exactly what I'm referring to when I say that I'm not sure how to get the data across. I would much, much rather build it like that, but I just don't understand (in this instance) how to get the database object into the model without passing anything - basically, exactly that example.

What would the StatusModel class look like?
Was This Post Helpful? 0
  • +
  • -

#8 Atli   User is offline

  • Enhance Your Calm
  • member icon

Reputation: 4241
  • View blog
  • Posts: 7,216
  • Joined: 08-June 10

Re: MVC Forum Submission help

Posted 19 November 2015 - 02:08 PM

You'll always have to pass the database object into the model somehow. There is no way of getting around that, even if you go the route of using a Singleton, or something of that kind. (Which I don't really recommend.)

Larger frameworks take care of that kind of bother for you, by managing all of this behind the scenes. If I keep using Symfony as an example, a model class like that would typically be defined as a service, making it accessible through the container in the controller.
/** @var FooBundle\Model\StatusModel $model */
$model = $this->container->get("foo.model.status");
$status = $model->getDefaultStatus();


The model class itself would be defined to take in the database object (EntityManager, generally, in the case of Symfony) either through the constructor or through an accessor method.
<?php
namespace FooBundle\Model;

use Doctrine\ORM\EntityManager;

class StatusModel
{
    /** @var  EntityManager */
    private $em;
    
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    /**
     * Finds some sort of default status.
     */
    public function getDefaultStatus()
    {
        return $this->em->getRepository("FooBundle:Status")->findOneBy([
            "isDefault" => true
        ]);
    }
}


The model would be linked up with the container through configuration files, where you would specify what database object (EntityManager, or whatever you are using) should be provided for the constructor.
# FooBundle/Resources/config/services.yml

services:
    foo.model.status:
        class: FooBundle\Model\StatusModel
        arguments: ["@doctrine.orm.entity_manager"]




This keeps the controllers very clearly separated from the database layer. The controller actions themselves are never even aware of which database connection is being used to spawn the models, they just call them from the container and leave it up to the configuration.
Was This Post Helpful? 0
  • +
  • -

#9 Atli   User is offline

  • Enhance Your Calm
  • member icon

Reputation: 4241
  • View blog
  • Posts: 7,216
  • Joined: 08-June 10

Re: MVC Forum Submission help

Posted 19 November 2015 - 04:15 PM

I realize belatedly that the above post isn't altogether useful to your specific situation. More in a theoretical sense than a practical one, I suppose.

It's not overly hard to make a simplified version of this, though, that can be used in smaller frameworks with less "under-the-hood" stuff going on than large frameworks like Symfony.

Consider a (kind of) factory class like this:
<?php
namespace MyCustomFramework\Core;

class ModelFactory
{
    /* *************************************************
     * Singleton Methods - To simplify access to the
     * factory, since there is no bootstrapping to be
     * counted on.
     * *************************************************/

    /** @var ModelFactory */
    private static $factory;

    /**
     * @param \PDO $pdo
     * @param string $baseNamespace
     */
    public static function initialize(\PDO $pdo, $baseNamespace="\\")
    {
        if (!(self::$factory instanceof ModelFactory)) {
            self::$factory = new ModelFactory($pdo, $baseNamespace);
        }
    }

    /**
     * Get an instance of the request model.
     * Model path should look like: "FooBundle:BarModel"
     *
     * @param string $modelPath
     * @return object
     */
    public static function getModel($modelPath)
    {
        return self::$factory->_getModel($modelPath);
    }

    /* *************************************************
     * Instance Methods
     * *************************************************/

    private $db;
    private $baseNamespace;
    private $instanceRegistry;

    private function __construct(\PDO $pdo, $baseNamespace)
    {
        $this->db = $pdo;
        $this->baseNamespace = $baseNamespace;
        $this->instanceRegistry = [];
    }

    private function _getModel($modelPath)
    {
        if (!isset($this->instanceRegistry[$modelPath])) {
            $ns = $this->_parsePath($modelPath);
            $this->instanceRegistry[$modelPath] = new $ns($this->db);
        }
        return $this->instanceRegistry[$modelPath];
    }

    private function _parsePath($path)
    {
        list($bundle, $modelName) = explode(":", $path);
        $ns = $this->baseNamespace . "\\" . $bundle . "\\Model\\" . $modelName;
        if (class_exists($ns)) {
            return $ns;
        } else {
            throw new \RuntimeException("Invalid module path.");
        }
    }
}



It assumes a namespace structure for the framework that is identical to that of Symfony, though that is easily changed.

So assuming you had model classes located in places like:
- DIC\BlogApp\AdminBundle\Model\UserModel
- DIC\BlogApp\BlogBundle\Model\PostModel

You could use this factory class in your framework in two simple steps:

  • First, initialize it somewhere near the main entry point of your application. Perhaps in the main script, or a router, or even a base controller constructor. Something like that.
    $db = new \PDO("mysql:etc...");
    ModelFactory::initialize($db, "DIC\\BlogApp");
    
    


  • And then you can use the model factory anywhere, like in your controllers:
    $model = ModelFactory::getModel("BlogBundle:PostModel");
    $posts = $model->loadPage($pageNumber);
    
    


Just like with the Symfony code in the previous post, the controller is now completely separated from the data layer, and each model class is isolated, easily reusable and testable.
Was This Post Helpful? 0
  • +
  • -

#10 chris98   User is offline

  • D.I.C Lover

Reputation: 41
  • View blog
  • Posts: 1,112
  • Joined: 06-July 13

Re: MVC Forum Submission help

Posted 20 November 2015 - 09:25 AM

Ok, I think I'm finally starting to understand this now. I'm trying to keep to the basics still, and I do intend to use some kind of framework when I know this a bit better - but as a very basic design, is there anything that is fundamentally wrong, or could be better with this:

<?php
class search_controller extends base_controller
{
	public function __construct($registry)
	{
		$this->registry = $registry;
		$this->template = $registry->template;

		$this->search_model = new search_model($registry);
	}

	public function index_action()
	{
		$this->template->header = array(
			'title' => 'Search',
		);

		$this->template->data = array(
			'keywords' => $this->search_model->keywords,
			'search_type' => $this->search_model->search_type,
			'sections' => $this->search_model->get_form_options(),
		);

		$this->template->render('search_form');
	}

	public function search_action()
	{
		$this->search_model->search();
		$this->template->data = array(
			'num_results' => forum_number_format($this->search_model->count),
			'results' => $this->search_model->get_results(),
			'keywords' => $this->search_model->keywords,
			'sections' => $this->search_model->sections,
			'search_type' => $this->search_model->search_type,
			'protocol' => get_current_protocol(),
			'pagination' => pagination($this->search_model->limit, $this->search_model->page, '?section=search&act=search&amp;keywords='.urlencode($this->search_model->keywords).'&amp;author='.urlencode($this->search_model->author).'&amp;search_in='.$this->search_model->search_in.'&amp;sections%5B%5D='.implode("&amp;sections%5B%5D=", array_map('urlencode', $this->search_model->sections)).'&amp;search='.$this->search_model->search_type.'&amp;p=', $this->search_model->count),
		);

		$this->template->header = array(
			'title' => 'Search Results',
		);

		$this->template->render('search');
	}
}


<?php
class search_model
{
	var $limit = 10;
	public function __construct($registry)
	{
		$this->registry = $registry;
		$this->db = $registry->db;
		$this->user = $registry->user;
		$this->config = $registry->config;
		$this->cache = $registry->cache;
		$this->shn_common = $registry->shn_common;
		$this->lang = $registry->lang;
		$this->set_data();

		if (!$this->user['g_search'])
			new message($registry, $this->lang['No permission']);
	}

	public function set_data()
	{
		$this->keywords = isset($_GET['keywords']) ? urldecode($_GET['keywords']) : '';
		$this->search_type = isset($_GET['search']) && $_GET['search'] == '1' ? '1' : '0';
		$sections = isset($_GET['sections']) && is_array($_GET['sections']) ? array_filter(array_map('urldecode', $_GET['sections'])) : array();
		$this->sections = ($this->search_type == '1') ? array_map('panther_trim', $sections) : array_map('intval', $sections);
		$this->author = isset($_GET['author']) ? panther_trim($_GET['author']) : '';
		$this->search_in = (!isset($_GET['search_in']) || $_GET['search_in'] == '0') ? 0 : (($_GET['search_in'] == '1') ? 1 : -1);
		$this->page = isset($_GET['p']) ? intval($_GET['p']) : 1;
		$this->start = ($this->page - 1) * $this->limit;
	}

	public function get_form_options()
	{
		$sections = array();
		if ($this->search_type == '1')
		{
			$this->cache->get('sites_info2');
			foreach ($this->registry->links as $section)
				if ($section['make_category'] == '1')
					$sections[] = array('id' => $section['title'], 'title' => $section['title']);
		}
		else
		{
			$this->cache->get('download_sections2');
			foreach ($this->registry->sections as $section)
				$sections[] = array('id' => $section['id'], 'title' => $section['title']);
		}
		
		return $sections;
	}

	public function search()
	{
		if ($this->user['is_bot'])
			new message($this->registry, $this->lang['No permission']);

		if (!$this->keywords && !$this->author)
			new message($this->registry, 'You have to enter at least one keyword and/or an author to search for.');

		if ((!empty($this->sections) || (empty($this->sections) && $this->config['o_search_all_forums'] == '0' && !$this->user['is_admmod'])))
		{
			for ($i = 0; $i < count($this->sections); $i++)
			{
				$markers[] = '?';
				$this->data[] = $this->sections[$i];
			}
			$this->sql[] = " AND site IN (".implode(',', $markers).")";
		}

		// Flood protection
		if ($this->user['last_search'] && (time() - $this->user['last_search']) < $this->user['g_search_flood'])
				new message($this->registry, 'At least '.$this->user['g_search_flood'].' seconds have to wait between searches. Please wait another '.($this->user['g_search_flood'] - (time() - $this->user['last_search'])).' seconds and try again.');

		$update = array(
			'last_search' => time(),
		);
			
		$ident = array(
			':id' => (($this->user['is_guest']) ? get_remote_address() : $this->user['username']),
		);

		if (!$this->user['is_guest'])
			$this->db->update('users', $update, 'id=:id', $ident);
		else
			$this->db->update('online', $update, 'ident=:id', $ident);

		if ($this->author != '')
		{
			$select = array(
				':username' => $this->author,
			);

			$ps = $this->db->select('users', 'id', $select, 'username=:username');
			if (!$ps->rowCount())
				new message($this->registry, 'This user does not exist.');
			else
			{
				$this->sql[] = " AND userid=?";
				$this->data[] = $ps->fetchColumn();
			}
		}

		if ($this->search_type == '1')
			$this->search_articles();
		else
			$this->search_downloads();
	}

	public function get_results()
	{
		$results = array();
		foreach ($this->result as $cur_result)
		{
			$results[] = array(
				'title_url' => url_friendly($cur_result['title']),
				'id' => $cur_result['id'],
				'title' => $cur_result['title'],
				'posted' => format_time($cur_result['date'], true),
				'description' => $cur_result['article'],
			);
		}

		return $results;
	}

	private function search_articles()
	{
		if ($this->search_in > 0)
		{
			$this->sql[] = " AND text LIKE ?)";
			$this->data[] = '%'.$this->keywords.'%';
		}
		else if ($this->search_in == '0')
		{
			$this->sql[] = ") AND (title LIKE ? OR text LIKE ?)";
			$this->data[] = '%'.$this->keywords.'%';
			$this->data[] = '%'.$this->keywords.'%';
		}
		else
		{
			$this->sql[] = " AND title LIKE ?)";
			$this->data[] = '%'.$this->keywords.'%';			
		}

		$ps = $this->shn_common->select('pages', 'COUNT(id)', $this->data, "(sub_page!='sub_sub_page'".implode('', $this->sql));
		$this->count = $ps->fetchColumn();

		$this->data[] = $this->start;
		$this->data[] = $this->limit;

		$this->result = $this->shn_common->select('pages', 'title, id, article, date', $this->data, "(sub_page!='sub_sub_page'".implode('', $this->sql), 'id DESC LIMIT ?,?');
	}

	private function search_downloads()
	{
		if ($this->search_in > 0)
		{
			$this->sql[] = " AND text LIKE ?)";
			$this->data[] = '%'.$this->keywords.'%';
		}
		else if ($this->search_in == '0')
		{
			$this->sql[] = ") AND (name LIKE ? OR text LIKE ?)";
			$this->data[] = '%'.$this->keywords.'%';
			$this->data[] = '%'.$this->keywords.'%';
		}
		else
		{
			$this->sql[] = " AND name LIKE ?)";
			$data[] = '%'.$this->keywords.'%';			
		}

		$ps = $this->shn_common->select('downloads', 'COUNT(id)', $this->data, "(approval != '1'".implode('', $this->sql));
		$this->count = $ps->fetchColumn();

		$this->data[] = $this->start;
		$this->data[] = $this->limit;

		$this->result = $this->shn_common->select('downloads', 'name AS title, id, CONCAT(LEFT(text, 167), \' \') AS article, date', $this->data, "(approval != '1'".implode('', $this->sql), 'id DESC LIMIT ?,?');
	}
}

Was This Post Helpful? 0
  • +
  • -

#11 chris98   User is offline

  • D.I.C Lover

Reputation: 41
  • View blog
  • Posts: 1,112
  • Joined: 06-July 13

Re: MVC Forum Submission help

Posted 20 November 2015 - 10:34 AM

Another worry I have is whether my model classes are too short. Would it be acceptable if they only had one method in (bar __construct)?

Personally, I feel like this is a waste and would be unsure whether to combine such model class files or not.

This post has been edited by chris98: 20 November 2015 - 10:37 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1