-1

So, i'm working on building my first MVC based application.

I followed this tutorial: requiremind.com/a-most-simple-php-mvc-beginners-tutorial/ and now i'm creating a forum based on the above tutorial.

This is in my index.php

if(isset($_GET['controller']) && !isset($_GET['action'])) {
    $controller = $_GET['controller'];
    $action = 'topic';
} else {
    $controller = 'main';
    $action = 'index';
}

The first condition is for displaying pages with topic and will work for url with ?controller=PHP or something like this and set the $action as 'topic'.

Now the request will be transferred to routes.php

function call($controller, $action) {

    require_once('controllers/main_controller.php');

    if($controller == 'main') {
        require_once('models/main_model.php');
        $controller = new MainController();
    } else {
        require_once('models/topic_model.php');
        $controller = new MainController();
    }

    $controller->$action();
}

$db = Db::getInstance();
$query = $db->query('SELECT cat_name FROM category');
$categories = $query->fetchAll();

foreach($categories as $category_name) {
    if($controller == $category_name && $action == 'topic') {
        call($controller, $action);
    }
}

if(($controller == 'main') && ($action == 'index')) {
    call($controller, $action);
}

Now this will make a request with database and if the topic/category exists, it will call the function.

main_controller.php

class MainController {
    public function index() {
        $category = Main::home();
        require_once('views/home.php');
    }

    public function topic() {
        $topics = Topic::thread_topic($_GET['controller']);
        require_once('views/topic.php');
    }
}

After the main_controller.php, topic_model.php:

<?php

class Topic {
    public $thread_topic;
    public $thread_desc;
    public $thread_created_at;
    public $thread_created_by;
    public $category_id;

    public function __construct($thread_topic, $thread_desc, $thread_created_at, $thread_created_by, $category_id) {
        $this->thread_topic = $thread_topic;
        $this->thread_desc = $thread_desc;
        $this->thread_created_at = $thread_created_at;
        $this->thread_created_by = $thread_created_by;
        $this->category_id = $category_id;
    }

    public function thread_topic($controller) {
        $list = [];
        $cat_id = Fnct::cat_name_to_id($controller);
        $db = Db::getInstance();
        $stmt = prepare('SELECT * FROM thread WHERE thread_created_by =:cat_id');
        $stmt->execute(array(':cat_id' => $cat_id));
        foreach($stmt->fetchAll() as $thread) {
            $list[] = new Topic($thread['thread_topic'], $thread['thread_desc'], $thread['thread_created_at'], $thread['thread_created_by'], $thread['category_id']);
    }
        return $list[];
    }
}

?>

And now the views/topic.php

<table border='1'>

    <tr>
        <th>Thread Topic</th>
        <th>Thread Desc</th>
        <th>Thread Started At</th>
    </tr>

    <tr>
        <?php foreach($topics as $topic) { ?>
        <td><?php echo $topic->thread_topic; ?></td>
        <td><?php echo $topic->thread_desc; ?></td>
        <td><?php echo $topic->thread_created_at; ?></td>
        <?php } ?>
    </tr>

</table>

The problem is this, it is displaying blank page. And giving no error.

And i'm still struggling to figure out what is wrong here. Please tell me what i did wrong here??

Edit: In routes.php moves $controller = new MainController() under if statement. But still same blank page is displaying.

tereško
  • 58,060
  • 25
  • 98
  • 150
Deepak Rawat
  • 131
  • 2
  • 10
  • Probably a 500 error (white screen of death), you need to enable `display_errors` in php.ini if you want to see the errors or look in your error logs. – naththedeveloper Feb 07 '18 at 07:43
  • @naththedeveloper No, it is not a blank page. It is loading the layout.php with is required in index.php but not loading the final view page. – Deepak Rawat Feb 07 '18 at 07:47
  • The problem you have is in your router. The code you wrote there is really weird. You define `$controller` as a new `MainController` object, but one line later you test if `$controller == main` .. that doesn't make any sense. You should review the tutorial on how they created the router. (Btw. I started with the same tutorial some months ago and I'm still building applications on top of that MCV example). – Twinfriends Feb 07 '18 at 07:59
  • @Twinfriends The test is to check the url. If the url is blank/homepage it will set it to 'main' and if the url is /?controller=PHP it will set it to 'PHP' that is the name of the topic in forum. For more clarification check the URL that is mentioned above. You will get the idea of it. – Deepak Rawat Feb 07 '18 at 08:01
  • No. You're wrong. In theory you're right, but in praxis... just look at your code. You create a `new MainController` and assign that to the variable `$controller` - 1 line after that you tst `if($controller == main)` - That will NEVR be true!!! This test is completly useless. You can't assign a object to a variable and then test the variable for a string. Thats just not logic at all. As said, I work since a few months with this MVC example, I know how its supposed to work. – Twinfriends Feb 07 '18 at 08:06
  • @Twinfriends Sorry, but it will first check the condition and if true the only it will call the function 'call'. And only after that it will set that. You are missing that call function is call after the comparison. – Deepak Rawat Feb 07 '18 at 08:10
  • I'm talking about line 4 & 5 of your routes.php - Inside the `call` function - I'm 100 % sure I'm right. just look at your code. `require_once('controllers/main_controller.php'); $controller = new MainController(); if($controller == 'main') {` can't really do more than say it 3 times...... – Twinfriends Feb 07 '18 at 08:12
  • @Twinfriends Please once check that tutorial in the URL given above. I still in learning mode. Maybe you are right. I followed that tutorial and based on that i created this. Please check that once. – Deepak Rawat Feb 07 '18 at 08:21
  • 1
    As said I work with that tutorial since moths. You're wrong. 100 %. I told you whats wrong. You don't show any effort to fix that probolem. I'll write an answer now and try to show you your mistakes, but PLESE do some basic tutorials on PHP and SQL, since I don't think you feel really confident with. – Twinfriends Feb 07 '18 at 08:23
  • @Twinfriends OKAY. Now it get it. I fixed that but still it is giving same error. I moved that $controller = new MainController(); inside the if statements. But it is still same. – Deepak Rawat Feb 07 '18 at 08:24
  • @DeepakRawat See my answer. And yes, with the $controller = new MainController() INSIDE the if function you're good, but you don't want to load always the same controller. As said, you have to learn about MVC pattern. You want to lead a controller based on what you need. You don't want to have a controller thats always loaded, thats not what MVC is for. – Twinfriends Feb 07 '18 at 08:35
  • @DeepakRawat FYI: that tutorial you picked is actually actively harmful and teaches you a lot of bad practices in general, while completely misrepresenting what MVC is. You should avoid that abomination at all costs. – tereško Feb 07 '18 at 11:46
  • @tereško Yes, in comments of that tutorial, it is pointed out by other guys that separation of concern is not done correctly. But i think it is OKAY for a newbie like me to learn from it. – Deepak Rawat Feb 07 '18 at 12:18
  • @DeepakRawat it's not just the complete misrepresentation of SoC, that is the problem. The other issues are: using singleton for DB, wrong PDO setup, abuse of `requrie_once`, no PSR4 compatibility, vulnerability to code injection, idiotic and unmaintainable routing, global state, "static classes" and leaking object encapsulation. Basically, every bit of code in that post contains one (or usually - more) bad practices. – tereško Feb 07 '18 at 12:27
  • @tereško Can you refer me any tutorial which i can follow?? – Deepak Rawat Feb 07 '18 at 14:40

1 Answers1

0

Well, your error is in your routes.php as mentioned above. I'll try to explain it here in an answer and also directly fix the mistakes you did.

Okay, lets start. First of all, lets ignore the call() function first and only look at the other part:

Your Code:

$db = Db::getInstance();
$query = $db->query('SELECT cat_name FROM category');
$categories = $query->fetchAll();

foreach($categories as $category_name) {
    if($controller == $category_name && $action == 'topic') {
        call($controller, $action);
    }
}

if(($controller == 'main') && ($action == 'index')) {
    call($controller, $action);
}

You should know that fetchAll() will always return an arrqay, not a string, thats why you can't compare the result directly with a string. Change the code to:

$db = Db::getInstance();
$query = $db->query('SELECT cat_name FROM category');
$categories = $query->fetchAll();

foreach($categories as $category_name) {
    if($controller == $category_name["cat_name"] && $action == 'topic') {
        call($controller, $action);
    }
}

if(($controller == 'main') && ($action == 'index')) {
    call($controller, $action);
}

You see the $categoriy_name["cat_name"] - You have to give the key, since $category_name is an array, not a string. (May look at some tutorials for PHP and SQL, like how all the fetch method works and what their output is).

And then the second part, your call function

Your code:

function call($controller, $action) {

    require_once('controllers/main_controller.php');
    $controller = new MainController();

    if($controller == 'main') {
        require_once('models/main_model.php');
    } else {
        require_once('models/topic_model.php');
    }

    $controller->$action();
}

So, you pass $controller as an argument to that function. Thats correct. But then you assign $controller = new MainController(); so $controller is no longer the string you passed to the function, but now ins an object of the class MainController. So you loose your value on what you passed to the function. And then only 1 Line after you do:

if($controller == 'main') {

This will never be true. At this point $controller is an object of your mainController() and the argument will always evaluate to false, so you'll always load the topic_model.php Since your topic() function is inside the MainController() its okay, but its not what MVC is for. Thats just.. some random code there. Thats not MVC, nor good practice.

But well, I don't know what you want to do there with your main controller, I'm not going to dive more into that topic. With the changes of your foreach-loop the call should work and your view should be loaded.

You don't want something like a MainController that is always laoded, or at least not for those methods you have inside - May look up some tutorials on MVC. You only wanna load the controller for the specific section you're in.

Anyway, I wish you all the best for the future and happy coding! :)

Twinfriends
  • 1,972
  • 1
  • 14
  • 34
  • If i'm using $category_name["cat_name"] it is giving error: Fatal error: Cannot use [] for reading in C:\xampp\htdocs\mvc-forum\models\topic_model.php on line 27 – Deepak Rawat Feb 07 '18 at 08:41
  • Then you should may check your database connection and make sure you get results from the query. Try to `var_dump($categories);` after the line with `fetchAll()` and tell me the output. – Twinfriends Feb 07 '18 at 08:44
  • Here is the result: array(4) { [0]=> array(2) { ["cat_name"]=> string(3) "PHP" [0]=> string(3) "PHP" } [1]=> array(2) { ["cat_name"]=> string(7) "Laravel" [0]=> string(7) "Laravel" } [2]=> array(2) { ["cat_name"]=> string(8) "Symphony" [0]=> string(8) "Symphony" } [3]=> array(2) { ["cat_name"]=> string(6) "Others" [0]=> string(6) "Others" } } – Deepak Rawat Feb 07 '18 at 08:46
  • @Twinfriends You should really work on that condescending tone... Keep it professional. – Yoshi Feb 07 '18 at 08:46
  • @Yoshi No buddy, its fine. Actually its my mistake and he is really helping me out. I know how frustrating it is to actually answer same thing again and again. – Deepak Rawat Feb 07 '18 at 08:49
  • @DeepakRawat No it's not fine. It sets an example for other visitors of this site. And this kind of tone is not what this SO should be. – Yoshi Feb 07 '18 at 08:52
  • @Yoshi I know it was a bit rude. Not having the best day. Also I excused at the end of my answer. Its jut that I have a bad day and then I said like 3 times the same in the comment section above and he didn't get it, so I guess there's some frustration in my answer. Still I try to help him. I don't want to hurt him. Not at all. And btw @Deepak: I try to solve the problem you have with the `Cannot use [] for reading` but I don't see it right now where the problem is, just copied your code to my local server and tested it.. works. What PHP Version are you using? – Twinfriends Feb 07 '18 at 08:52
  • @Yoshi Better now? Changed the whole answer. Peace and love. Btw thanks for the downvote! :) Really appreciate that. – Twinfriends Feb 07 '18 at 08:58
  • 1
    @Twinfriends Thanks for the update and your efforts! I removed my downvote. – Yoshi Feb 07 '18 at 09:01
  • 1
    @Yoshi Thanks. Sorry I really try to work on my behavior... its just not that easy on days like that. May I should simply leave SO for a day or two, think would be better. Thank you rememberd me to be a better example, because I actually don't like people who behave like me right now. – Twinfriends Feb 07 '18 at 09:04
  • @Twinfriends I'm using version: 5.6.31 – Deepak Rawat Feb 07 '18 at 09:10