Extending controller in Zend Framework 2 - php

Here is my app structure:
/application
/config
/library
/Foo
/Controler.php
/module
/User
/config
/src
/Bar
/Controler
/BarController.php
/public
/vendor
/init_autoloader.php
The Controler.php file...
namespace Foo_Controller;
use Zend\Mvc\Controller\AbstractRestfulController;
class Foo_Controller extends AbstractRestfulController {
protected $foo;
public function getFoo()
{
return "foooo";
}
function __construct()
{
parent::__construct();
$foo = $this->getFoo();
}
}
The BarController.php...
namespace Bar\Controler;
use Zend\Mvc\Controller\AbstractRestfulController;
use Foo_Controller\Foo_Controller;
use Zend\View\Model\JsonModel;
class BarController extends Foo_Controller {
.
..
....
}
Added the path /library folder in the init_autoloader.php
$loader = include 'vendor/autoload.php';
$zf2Path = 'vendor/zendframework/zendframework/library';
$loader->add('Zend', $zf2Path);
$loader->add('Julia', 'library'); // added the library folder
if (!class_exists('Zend\Loader\AutoloaderFactory')) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
I get an error 500 with the following:PHP Fatal error: Class 'Foo_Controller\Foo_Controller' not found in /application/module/Bar/src/Bar/Controller/BarController.php on line #
I really don't know what to do now. I have been searching the internet for some time now for the correct way to extend a controller class in Zend Frazmework 2, but i can't seem to grasp it!
What am i doing wrong in the app?
Thank you

I would suggest you set this out slightly differently. Would really make more sense to create your own custom module which you can load into any project with directory structure like:
/zf2-MyCustomModule
/src
/MyCustomModule
/Controller
/Abstract
/MyAbstractController.php
namespace for MyAbstractController.php would be - MyCustomModule\Controller\Abstract
If it is specific to the project then why not just add
/Abstract
/MyAbstractController.php
to the User Module Controller dir.
but seems like what you have done is pretty much right you would just need to update namespace in Foo_Controller.php to:
namespace Julia\Foo\Controller;
not
namespace Foo_Controller;
Though I have never used the method you are using so am not 100% sure.
I would add a new local config to /config/autoload/
like /config/autoload/namespaces.global.php
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
'Julia' => __DIR__ . '/Library',
),
)
));
then your namespace should still be Julia\Foo\Controller;

Related

Codeigniter 4: creating modules

I'm trying to create modules in Codeigniter 4 to work with HMVC. I tried following this user guide https://codeigniter4.github.io/userguide/general/modules.html, but cannot get it working.
I created a 'modules' folder, alongside the app, public, etc. folders.
Added to app/config/autoload.php
'Modules' => ROOTPATH.'modules'
Inside the modules folder, I created a 'Proef' folder, containing a Controllers folder and 'Proef.php' file.
The file contains the following;
namespace App\Modules\Proef\Controllers;
class Proef extends \CodeIgniter\Controller
{
public function index() {
echo 'hello!';
}
}
In the app/config.routes.php file I added
$routes->group('proef', ['namespace' => 'Modules\Proef\Controllers'], function($routes)
{
$routes->get('/', 'Proef::index');
});
Yet, the following error persists:
Controller or its method is not found: \Modules\Proef\Controllers\Proef::index
What am I missing?
If you put your modules folder "alongside" and not under your app folder, then your namespace is wrong.
So you would have something like
app/
Modules/ ==> can be modules or Modules but must be set in autoload with the same case
Proef/
Controllers/
Proef.php
NOTE: modules can be Modules or modules but the corresponding entry in the autoload must match.
For modules
'Modules' => ROOTPATH . 'modules'
For Modules
'Modules' => ROOTPATH . 'Modules'
It appears (from my limited testing) that the other folder names must
be 1st letter Upper case. This is under Apache on Linux.
let's use Modules for the folder name so in Autoload.php we would have...
$psr4 = [
'App' => APPPATH, // To ensure filters, etc still found,
APP_NAMESPACE => APPPATH, // For custom namespace
'Config' => APPPATH . 'Config',
'Modules' => ROOTPATH . 'Modules'
];
So your Proef Controller - Proef.php ... Note the namespace being used.
<?php
namespace Modules\Proef\Controllers;
use App\Controllers\BaseController;
class Proef extends BaseController {
public function index() {
echo 'Hello - I am the <strong>'. __CLASS__ . '</strong> Class';
}
}
To make this accessible via the URL you can set the routes (Routes.php) to... (simple version)
$routes->get('/proef', '\Modules\Proef\Controllers\Proef::index');
To make it callable within other Controllers... ( I have borrowed Home.php for this)
<?php namespace App\Controllers;
use \Modules\Proef\Controllers\Proef;
class Home extends BaseController
{
public function index()
{
$mProef = new Proef();
$mProef->index();
return view('welcome_message');
}
//--------------------------------------------------------------------
}
In your URL -
/proef will result in the just the message
/home will result in the class message and the welcome page.
So hopefully this will help you figure this out. its a lot of fun :)
Aside:
You can put your Modules Folder anywhere. I put mine under app/ for ole times sake, which removes the need to add the entry in Autoload.php as they fall under app/ which is already defined.
The namespace and use statement need to be changed appropriately as well.
Edit
namespace to Modules\Proef\Controllers in Proef class

How to make sure the correct class file is loaded when calling / extending class

I did brows through that question page but the answers given, I felt, didn't sufficiently answer the question I was struggling with.
I have a little php project. In this project I define two classes both in their own files. The second class extends the first class. In the app.php file I instantiate the second class and call a method.
├── Models/
│ ├── Class1.php
│ └── Class2.php
└── app.php
<?php
// Class1.php
namespace Models\Class1;
/**
* Class1 does things
*/
class Class1 {
public function someMethod() {
// code
}
}
<?php
// Class2.php
namespace Models\Class2;
use \Models\Class1\Class1;
include('./Class1.php');
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
<?php
// app.php
use Models\Class1\Class1;
use Models\Class2\Class2;
require('Models/Class1.php');
require('Models/Class2.php');
$c1 = new Class1();
$c2 = new Class2();
Now I'm not a really experienced php programmer but I thought I had a solid grasp of this class/namespace business and including/requiring files, but apparently I don't.
If I copy the methods I need from Class1 to Class2 and not extend/include/use Class2 everything works fine, but want to extend Class1, so I don't have to repeat myself! Damn it!
This is what I get when running the app.php file.
Warning: include(../Models/Class1.php): failed to open stream: No such file or directory in C:\wamp64\www\project\Models\Class2.php on line 8
Warning: include(): Failed opening '../Models/Class1.php' for inclusion (include_path='.;C:\php\pear') in C:\wamp64\www\project\Models\Class2.php on line 7
I've been reading all I can find online about this stuff, have written and rewritten my code in a dozen different ways but have yet to find a solution. Some help would be greatly appreciated!
Quick Answer
Unless you've specified the full path to the include file, PHP will always try to resolve files according to the location of the entry script. In your case, your entry script seems to be app.php at the root of the application.
So in your Class2.php, instead of include('./Class1.php');, you should write include('Models/Class1.php'); and things should work. But while it fix a short term problem, your code would be really non-portable. Besides, including the same file twice would result in another error (e.g. re-declaring class).
Slightly Smarter Approach
A slightly smarter would be to do this in Class2.php instead.
<?php
// Class2.php
namespace Models\Class2;
use \Models\Class1\Class1;
include_once __DIR__ . '/Class2.php';
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
The variable __DIR__ will always be resolved to the directory of the script file, not the entry script.
But again, it is clumsy and error prone to do file includes by hand.
An Even Smarter Approach: Autoload
PHP supports file autoloading when a class is declare. That is to have a piece of code to do the file include when a class is called. If you want to have fun, you're welcome to write your own autoloader function and register to spl_autoload_register. With a combination of the __DIR__ technique we talked about, you can easily resolve the autoloading path from namespace.
A quick and ugly autoloading app.php would probably look like this:
<?php
// app.php
use Models\Class1\Class1;
use Models\Class2\Class2;
spl_autoload_register(function ($class_name) {
$realClassName = basename(str_replace('\\', DIRECTORY_SEPARATOR, $class_name));
include_once __DIR__ . DIRECTORY_SEPARATOR . 'Models' . DIRECTORY_SEPARATOR . $realClassName . '.php';
});
$c1 = new Class1();
$c2 = new Class2();
The Smart Approach: Composer Autoload
If you're learning OOP PHP today, I'd highly recommend you to learn Composer and PSR-4. PSR-4 defines how you should structure a PHP application for class autoloading. Composer implements a PSR-4 autoloader by default.
First you should comply with the namespace standard. The least change to do is to loose the extra "Class1" and "Class2" in your namespace:
<?php
// Class1.php
namespace Models;
/**
* Class1 does things
*/
class Class1 {
public function someMethod() {
// code
}
}
<?php
// Class2.php
namespace Models;
use \Models\Class1;
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
With a nicely structured application folder, namespace structure and a correctly written autoload.psr-4 section in composer.json, composer will help you to generate a class autoloader. Your composer.json would probably look like this:
{
"autoload": {
"psr-4": {
"Model\\": "Model/"
}
}
}
You may now run composer dump-autoload to create the autoloader. Then you can simply add this to your entry script app.php. When things are ready, you may simply add use and new statement anywhere in the application.:
<?php
// app.php
use Models\Class1;
use Models\Class2;
require_once './vendor/autoload.php';
$c1 = new Class1();
$c2 = new Class2();
All includes are handled by the autoloader. Hands free.
Remove the line
include('./Class1.php');

Zf2 extending the class giving the error class not found

I am trying to create the library inside the vendor folder in ZF2. Here is the structure:
/Vendor
/Mylib
/Mylib.php
/MylibStore.php
/MylibError.php
....
I have declared the same lib in Applicaiotion/Module.php:
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
'Mylib' => __DIR__ . '/../../vendor/Mylib',
),
),
);
}
Now I am calling Mylib class in controller it is working but when I am trying to instantiate other class in controller it is giving the error. Here is snap of code:
Mylib.php
namespace Mylib;
abstract class Mylib
{
MylibStore.php
namespace MylibStore;
use \Mylib\MylibError;
class MylibStore extends MylibError
{
MylibError.php
namespace MylibError;
class MylibError
{
I am getting the following error :
Fatal error: Class 'MylibStore\MylibError' not found in
C:\xampp\htdocs\coxaxle\vendor\Mylib\MylibStore.php on line 5
Please let me know what I am doing wrong? And how can I resolve this issue?
Problem is in your namespaces. All your files inside Mylib directory should have same namespace- Mylib.
That's why only Mylib class works, because it has correct namespace.
If you put your classes in separate directories then you have to update namespace about this directory.
Example:
/Vendor
/Mylib
/Service
/MylibService.php
/Mylib.php
....
Class Mylib should have namespace Mylib
Class MylibService should have namespace Mylib\Service

register namespace in silex without composer

I'm using silex to build an api for an app I'm building, and I want to abstract my controllers into different files.
I work for a pretty big corporation with a very strict IT department, and they won't let me install composer. For this reason, I need to register namespaces manually, via the $loader->add() function, but I get the following error:
Fatal error: Class 'App\Controller\SessionController' not found in C:\path\to\webroot\App\bootstrap.php on line 9
Does anyone know why the autoloader isn't picking up my App namespace?
Currently, my app is structured as follows:
App/
Controller/
ApiControllerAbstract.php
SessionController.php
bootstrap.php
vendor/
www/
index.php
index.php
require_once "../app/bootstrap.php";
bootstrap.php
$loader = require_once __DIR__.'/../vendor/autoload.php';
$loader->add('App', __DIR__ . '/../App/');
$app = new Silex\Application();
$app->mount('/session', new App\Controller\SessionController());
$app->run();
SessionController.php
namespace App\Controller;
class SessionController extends ApiControllerAbstract {
public function connect( $app)
{
$this->app = $app;
$controller_collection = $app['controllers_factory'];
// DECLARE ALL YOUR ROUTES HERE
//...
}
}
Hello,
The following line:
$loader->add('App', __DIR__ . '/../App/');
Should be:
$loader->add('App', __DIR__ . '/../');

Zend Framework 2 Library Paths

Trying to get my feet wet on ZF2 and I've stumbled on my first problem. Say on a module I want to use Shanty_Mongo (an external library to connect to MongoDb)
So I've copied the entire Shanty directory on the library and created a new Model class:
namespace Dummy\Model;
use Shanty\Mongo\Document;
class Dummy extends Shanty_Mongo_Document {
public function setConnections( $connections ) {
Shanty_Mongo::addConnections($connections);
}
}
(The setConnections() is to be used by DI, if I've understood it well)
This seems to fail to find Shanty_Mongo_Document. Should I add something to the application.config.php to point to the extra library?
The library Shanty_Mongo is an "old" underscore separated library without using namespaces. In ZF2, the style is the same PSR-0 standard but with namespaces (so Shanty_Mongo will be Shanty\Mongo). However, you are able to load these old style fine with a classmap for example. Then you can use underscore separated classes inside your ZF2 project.
I'd suggest you create a module for this library and put that module under ./vendor (for "modules providing 3rd party features"). In this module, you can create the following directory structure (I assume the name of the module is ShantyMongo):
./vendor/ShantyMongo/
library/
Module.php
autoload_classmap.php
autoload_function.php
autoload_register.php
The library is a submodule to the Shanty-Mongo git repository. The file autoload_classmap.php is a classmap created by the php script classmap_generator.php inside the bin directory of the ZF2 repository. Then the autoload_function.php can be something simple as this:
<?php
return function ($class) {
static $map;
if (!$map) {
$map = include __DIR__ . '/autoload_classmap.php';
}
if (!isset($map[$class])) {
return false;
}
return include $map[$class];
};
And autoload_register.php something like this:
<?php
spl_autoload_register(include __DIR__ . '/autoload_function.php');
To let the ZF2 application know you have this module, you need to fill the module.php with a ShantyMongo\Module class. Something like this should be sufficient:
<?php
namespace ShantyMongo;
use Zend\Module\Consumer\AutoloaderProvider;
class Module implements AutoloaderProvider
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
)
);
}
}
If you add "ShantyMongo" to your modules array in application.config.php you now have set up the autoloader for this 3rd party library inside ZF2. You can then use your model as follows:
<?php
namespace Dummy\Model;
class Dummy extends Shanty_Mongo_Document {
public function setConnections ($connections) {
Shanty_Mongo::addConnections($connections);
}
}
Because ShantyMongo doesn't use namespaces, you don't have that use statement anymore.

Categories