I have this folder structure for mvc:
application
-----------catalog
------------------controller
----------------------------IndexContoller.php
------------------model
----------------------------IndexModel.php
------------------view
------------------language
-----------admin
------------------controller
------------------model
------------------view
------------------language
core
------Controller.php
public
------Index.php
vendor
....
In index.php I have:
define('DS', DIRECTORY_SEPARATOR, true);
define('BASE_PATH', __DIR__ . DS, TRUE);
//Show errors
//===================================
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
define('ABSPATHS', preg_replace('/\\\/', '/', dirname(dirname(__FILE__))));
$abspath = preg_replace('/\\\/', '/', dirname(__FILE__));
require '../vendor/autoload.php';
//Upadte
$templates = new League\Plates\Engine(Config::get('PATH_VIEW'));
and autoload using composer PSR4 like this :
"autoload": {
"psr-4": { "Application\\": "application/","Application\\Core\\": "application/core/","Application\\Catalog\\Model\\": "application/catalog/model/"}
}
}
Now in Core Controller I have:
namespace Application\Core;
class Controller
{
// public $templates;
/**
* Construct the (base) controller. This happens when a real controller is constructed, like in
* the constructor of IndexController when it says: parent::__construct();
*/
public function __construct()
{
$this->Language = new Language();
}
}
In controller IndexController I have:
namespace Application\Catalog\Controller;
use Application\Core\Controller as Controller;
class IndexController extends Controller
{
/**
* Construct this object by extending the basic Controller class
*/
public function __construct()
{
parent::__construct();
}
/**
* Handles what happens when user moves to URL/index/index - or - as this is the default controller, also
* when user moves to /index or enter your application at base level
*/
public function index()
{
echo $templates->render('index/index', ['name' => 'Jonathan']);
}
}
In Action i see this error and autoload not loading class in controller:
Notice: Undefined variable: templates in /Applications/XAMPP/xamppfiles/htdocs/cms/application/catalog/controller/IndexController.php on line 23
How do can i fix this error?
The error states that Application\Core\League\Plates\Engine can not be found. According to your autoload definition Application\Core is located at application/core but according to you directory structure you supplied that file doesn't exists. You need to ensure the file you're trying to load exists where you've said it is.
As a side note you're using composer's PSR-4 autoload functionality but you aren't conforming to PSR-4
You're not explicitly listing vendor folder contents, but I suppose you installed the package https://github.com/thephpleague/plates/blob/master/composer.json through composer... then:
How about placing in Controller :
/Applications/XAMPP/xamppfiles/htdocs/cms/application/core/Controller.php
on top the line:
use League\Plates\Engine;
Also:
"autoload": {
"psr-4": { "Application\\": "application/","Application\\Core\\": "application/core/","Application\\Catalog\\Model\\": "application/catalog/model/"}
}
}
is too much...
The following should be enough:
autoload": {
"psr-4": {
"Application\\": "application/"
}
}
Then add directives like (when needed) in your classes:
use Application\Catalog\Model\...
EDIT
Var $templates is not accessible within IndexController, try this:
remove form index.php
$templates = new League\Plates\Engine(Config::get('PATH_VIEW'));
and edit IndexController as follows:
namespace Application\Catalog\Controller;
use Application\Core\Controller as Controller;
use League\Plates\Engine;
class IndexController extends Controller
{
/**
* Construct this object by extending the basic Controller class
*/
public function __construct()
{
parent::__construct();
}
/**
* Handles what happens when user moves to URL/index/index - or - as this is the default controller, also
* when user moves to /index or enter your application at base level
*/
public function index()
{
$templates = new Engine(Config::get('PATH_VIEW'));
echo $templates->render('index/index', ['name' => 'Jonathan']);
}
}
But since I suppose you'll need to use $templates in more controllers you should define
protected $templates = new League\Plates\Engine(Config::get('PATH_VIEW'));
in parent class Controller and access to it through
$this->templates
in descendants...
Related
I'm using the latest 'master' branch of CodeIgniter 4
I have a Library that I'm trying to load automatically. Effectively, I want to have have 'one' index.php (that has meta, the basic html structure, etc) through which I can load views via my 'Template' Library.
My Library file: (~/app/Libraries/Template.php)
//class Template extends CI_Controller
class Template {
/* This throws an error, but I will open up a separte thread for this
public function __construct() {
parent::__construct();
}
*/
public function render($view, $data = array()) {
$data['content_view'] = $view;
return view('layout/index', $data);
}
}
I also have a controller set up:
class Locations extends BaseController
{
public function index()
{
return $this->template->render("locations/index", $view_data);
//return view('locations/index');
}
//--------------------------------------------------------------------
}
In ~/app/Config/ I added my Library
$classmap = [
'Template' => APPPATH .'/Libraries/Template.php'
];
I'm getting the following error:
Call to a member function render() on null
What am I doing wrong that's causing my library not to load?
In CI4 the BaseController is where you create things that you want to be used by multiple other controllers. Creating classes that extend others is so very easy in CI4.
It seems to me that the only thing you are missing is creating the Template class. (There are a couple of other minor things too, but who am I to point fingers?)
One big item that might be just that you don't show it even though you are doing it. That is using namespace and use directives. They are must-do items for CI 4.
Because of where you have put your files you don't need and should remove the following. See how I've used use which imports namespace already known to the autoloader.
$classmap = [
'Template' => APPPATH .'/Libraries/Template.php'
];
First, the BaseController
/app/Controllers/BaseController.php
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use App\Libraries\Template;
class BaseController extends Controller
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* #var array
*/
protected $helpers = [];
protected $template;
/**
* Constructor.
*/
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
$this->template = new Template();
}
}
/app/Controllers/Locations.php
class Locations extends BaseController
{
public function index()
{
// set $viewData somehow
$viewData['someVar'] = "By Magic!";
return $this->template->render("locations/index", $viewData);
}
}
/app/Libraries/Template.php
<?php namespace App\Libraries;
class Template
{
public function render($view, $data = [])
{
return view($view, $data);
}
}
/app/Views/locations/index.php
This works as if... <strong><?= $someVar; ?></strong>
I know I haven't created exactly what you want to do. But the above should get you where you want to go. I hope so anyway.
It's tricky at first.
But I managed to run it successfully
Make sure you give it proper namespace
And then just "use" in your controller Location.
I dont change anything on Autoload.php.
app/Libraries/Template.php
<?php
namespace App\Libraries;
class Template {
public static function render($param) {
return 'Hello '.ucwords($param);
}
}
The proper way to call is put use App\Libraries\Template just before class Location extends BaseController
app/Controllers/Locations.php
<?php
namespace App\Controllers;
use App\Libraries\Template;
class Locations extends BaseController {
public function index() {
$template = new Template();
$renderedStuff = $template->render('World!');
echo $renderedStuff;
}
}
How does this work?
Notice in Template.php there is a namespace namespace App\Libraries;, so CI4 will automatically load that library properly also recognize the "Template" class. That is proper way to create CI4 libraries in my point of view.
How do we use that library?
Look at my example of Locations.php and then see this code use App\Libraries\Template;, that's how we call that libraries.
How do we call the function?
Look inside the index() function, here we call class Template using var $template = new Template();.
Then we call render() function in Template library with $template->render('World!');.
Just as simple as that.
I hope thats help, lemme know if it doesnt works. :)
Just a little hint, as my eye was hooked by the CI_Controller part.
You seems to use CI3 syntax within CI4, at least about loading the view, which translates to just:
$data = [
'title' => 'Some title',
];
echo view('news_template', $data);
See the CI3doc vs the CI4doc , the "static page" tutorial.
I am trying to create an ACL component as a service, for a multi-module PhalconPHP application. When I call the ACL component from the Controller Base, I am getting an error that I can't re-declare the ACL class.
Any ideas how to fix it, and understand the logic of why it is re-initialized again?
Fatal error: Cannot declare class X\Acl\Acl because the name is already in use in C:\xampp\htdocs\x\app\common\Acl\Acl.php on line 12
Update:
If I changed everything to "Pacl" then it works. I assume there might be a mixup with the PhalconPHP namespace. I am either not using the namespaces properly, or there's a bug in PhalconPHP 2.1 Beta 2.
/app/common/Acl/Acl.php
namespace X\Acl;
use Phalcon\Mvc\User\Component;
use Phalcon\Acl;
use Phalcon\Acl\Role as AclRole;
use Phalcon\Acl\Resource as AclResource;
/*
* ACL component
*/
class Acl extends Component {
private function initialize() {
}
public function isAllowed() {
die('called');
}
}
/app/front/controllers/ControllerBase.php
namespace X\Front\Controllers;
use Phalcon\Session as Session;
use Phalcon\Mvc\Controller;
use Phalcon\Mvc\Dispatcher;
class ControllerBase extends Controller {
public function beforeExecuteRoute(Dispatcher $dispatcher) {
//$this->acl = $this->getDI()->get("acl");
var_dump($this->acl->isAllowed()); //same behavior in both case
}
}
/app/front/Module.php
namespace X\Front;
use Phalcon\DiInterface;
use Phalcon\Mvc\Dispatcher;
use X\Acl\Acl as Acl;
class Module {
public function registerAutoloaders() {
$loader = new \Phalcon\Loader();
$loader->registerNamespaces(array(
'X\Front\Controllers' => __DIR__ . '/controllers/',
'X\Front' => __DIR__,
'X' => __DIR__ . '/../common/'
));
$loader->register();
}
public function registerServices(DiInterface $di) {
$di['acl'] = function() {
return new Acl();
};
}
}
This is not Phalcon issue. Look closely at your code:
namespace X\Acl;
use Phalcon\Acl;
class Acl extends ... {
}
What Acl interpreter should use? X\Acl\Acl or Phalcon\Acl?
The same error you get for example for the following code
namespace My\Awesome\Ns;
use Some\Name; # Name 1
class Name # Name 2
{
}
I create a new object $Manager in my index.php($Manager = new Manager()). After that I do some routing etc. to eventually end up in a controller. That controller includes the view of choice.
Now; I'd like to use $Manager in that view, without having to assign a global $Manager in the method that includes it.
My controller looks like this:(stripped all the other methods)
<?php
Class PostController extends Controller {
public function home () {
$items = $this->getItems();
require_once(ROOT . 'application/items/post/home.view.php');
}
}
?>
In that home.view.php file I include view.view.php in a loop and do:
<?= $Manager->currentPageItem(); ?>
but it gives the error:
<b>Notice</b>: Undefined variable: Manager in <b>C:\xampp\htdocs\blurp\application\items\post\view.view.php</b> on line <b>4</b><br />
I've tried making $Manager a global in the index.php, to no avail. adding global $Manager to the home method works; but is not my solution of choice(Since every method would need that than)
How can I get includes from functions to keep using global variables like my $Manager?
Instead of using globals use dependency injection
//Your controller
class PostController extends Controller {
public function home() {
$items = $this->getItems();
require_once(ROOT . 'application/items/post/home.view.php');
}
}
//Somewhere in low end part of code
class Controller extends BaseClass {
}
abstract class BaseClass {
public $manager;
/**
* Passing dependencies
*/
public function init(Manager $manager) {
$this->manager = $manager;
}
}
//Index, router, whatever
$controller = new PostController;
$controller->init(new Manager);
$controller->home();
//View
echo $this->manager->currentPageItem();
I have write a router.test.php file:
class RouterTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
require_once '../router.class.php';
}
public function testUnit()
{
$this->assertEquals(true, true);
}
}
... for simply test if my PHPUnit library is working correctly. But when I type phpunit router.test.php in my console I get this error message: Fatal error: Trait 'Core' not found in C:\xampp\htdocs\_Framework\ver_3\libraries\router\router.class.php on line 23
... my Router class is working correctly and my Autoloader automatically include trait (named Core) inside my Router class.
Where is the problem?
My Autoloader register method looks like:
public static function register()
{
return spl_autoload_register(
__CLASS__ . '::load'
);
}
... I use this method in my index.php file, should I write another autoloader for PHPUnits?
I have include necessary files in the setUp method, now the class looks like that:
<?php
use Framework\Libraries\Configuration\Configuration;
use Framework\Libraries\Autoload\Autoloader;
class RouterTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
require '../../core.trait.php';
require '../../configuration/configuration.class.php';
require '../../autoload/autoloader.class.php';
Configuration::set('files', [
require '../../../configuration/framework.configuration.php',
require '../../../configuration/autoloader.configuration.php',
require '../../../configuration/router.configuration.php',
]);
Autoloader::set('prefixes', ['Framework']);
Autoloader::set('extensions', ['.class.php', '.trait.php']);
Autoloader::register();
require_once '../router.class.php';
}
public function testUnit()
{
$this->assertEquals(true, true);
}
}
... an I get a new error message: E Time: 109 ms, Memory: 1.75Mb There was 1 error, but I can't get the error message. I think it's because Autoloader can't load files because of wrong path.
No it won't load index file automatically. So you need to include all your relevant class in your unit test class by adding include_once('AutoLoader.php'); in or another way is to create a bootstrap.php file & include all classes you need in all the PHPUnit Test class.
Reference.
I am using PHP ActiveRecord with my small MVC framework thtat includes an autoloader. In my controller I access the model Pub::find(64) for example.
My problem is that Pub::find(64) is inheritating the namespace of the controller and I get the error
Fatal error: Class 'App\Controllers\Pub' not found in /home/i554246/public_html/mvc/App/Controllers/Index.php on line 27
Pub is the Module name. The file get included ok. I can solve this issue by appending \Pub::find(64) but this is not really intuitive for new people on the project.
Is there a way to stop the namespace appending for the Pub::find(64) without altering that line?
Index Controller
namespace App\Controllers;
class Index extends \Core\Controller
{
protected
$title = 'Home'
;
/**
* Default action
*/
public function index()
{
// Pass the data to the view to display it
$this->view->set('testdb', \Pub::find(64));
}
}
App.php
/**
* Class autoloader
* #param $className
* #see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
*/
public function autoload($className)
{
preg_match('/(^.+\\\)?([^\\\]+)$/', ltrim($className, '\\'), $match);
//Build namespace Autoloading
$file = str_replace('\\', '/', $match[1]) . str_replace('_', '/', $match[2]) . '.php';
//Build Model path
$model = 'App/Models/' . $match[2] . '.php';
if ( is_file($file) ) {
require $file;
}elseif ( is_file($model) ) {
require $model;
}
}
Models/Pub.php
class Pub extends ActiveRecord\Model
{
}
It seems you don't understand namespaces.
Since you are using namespace App\Controllers, the global namespace is denoted with \.
So if you don't want to use fully-qualified name \Pub you have to put use declaration below the namespace, eg:
namespace App\Controllers;
use Pub;
PS: It's a good practice to put your models in a namespace too.