Search code examples
phpcomposer-phpautoload

Composer autoloader duplicating class file path for child class


I am using the composer autoloader (psr-4) to load my classes when I need them. I am running into an issue when I call a class from inside another class such as a model for a controller.

It works fine when I need to call the classes on their own but when I try to call one from inside the other it doesn't seem to work.

My composer.json has the following for the autoloading:

"autoload": {
    "psr-4": {
        "WhizzKids\\Controller\\": "controllers/",
        "WhizzKids\\Model\\": "models/"
    }
},

My controller is is called like so $AccountsController = new WhizzKids\Controller\AccountsController(); and the code in this file is:

<?php
namespace WhizzKids\Controller;

class AccountsController {
    private $AccountsModel;

    public function __construct() {
        $this->AccountsModel = new WhizzKids\Model\AccountsModel();
    }

    public function getAccounts() {
        return $this->AccountsModel->getAccounts();
    }
}

Within this controller I am trying to call my model which includes the logic for fetching from the database. The code in the model is:

<?php
namespace WhizzKids\Model;

class AccountsModel {
    private $conn;
    
    public function __construct() {
        $this->conn = mysqli_connect($_ENV['DB_SERVER'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_DATABASE']);
        if (!$this->conn) {
            die("Connection failed: " . mysqli_connect_error());
        }
    }

    public function getAccounts() {
        $sql = 'SELECT * FROM accounts';
        $result = mysqli_query($this->conn, $sql);
        $accounts = [];
        if(mysqli_num_rows($result) > 0) {
            // output data of each row
            while($row = mysqli_fetch_assoc($result)) {
                $accounts[] = $row;
            }
        } 
        mysqli_close($this->conn);
        return $accounts;
    }
}

When I try to load the view that uses this on the front-end i get the following error: error message

As you can see in the error message it seems to be prepending the previous namespace onto the child namespace, not sure where i'm going wrong? (also open to any improvements)


Solution

  • Your problem is related to how namespaces and classes are referred to in PHP. When you're inside a namespace and you try to instantiate a class with a new keyword, PHP thinks you're referring to a class that exists within the current namespace.

    If you want to refer to a class in a different namespace, you have two options. You can either use the fully qualified class name (including the leading ), or you can include a use statement at the top of your file.

    So, you could update your AccountsController class as follows:

    <?php
    namespace WhizzKids\Controller;
    
    use WhizzKids\Model\AccountsModel;
    
    class AccountsController {
        private $AccountsModel;
    
        public function __construct() {
            $this->AccountsModel = new AccountsModel();
        }
    
        public function getAccounts() {
            return $this->AccountsModel->getAccounts();
        }
    }
    

    Or you could use the fully qualified name in your __construct function:

    public function __construct() {
        $this->AccountsModel = new \WhizzKids\Model\AccountsModel();
    }
    

    Remember, when you are using namespaces in PHP, if you want to refer to a global class (like those in the root namespace, including built-in PHP classes), you need to precede them with a . So if you want to use a global class in your namespace, for example, the Exception class, you would need to refer to it as \Exception.

    The same principle applies here. By using \WhizzKids\Model\AccountsModel, you're saying "look for this class in the root () namespace, then in the WhizzKids\Model namespace".