So I am working on a sample database project.
I have a LoginController.php, a Faculty database, and a login page (phtml).
I get the error Fatal error: Class 'Faculty_DB' not found in /usr/local/zend/apache2/htdocs/InternProject1/application/controllers/LoginController.php on line 25
In the LoginController.php I have the following (plus some more):
public function indexAction()
{
$login = new Form_Login();
//$this->view->login = $login;
$request = $this->getRequest();
if($request->isPost())
{
$data = $request->getPost();
//$this->isValid(
if($this->getRequest()->getPost())
{
$username = $request->getParam('username');
$password = $request->getParam('password');
// echo " What you entered is: $username and $password";
//line 24
$faculty = new Faculty_DB();
//then conditions for validation.
This references
class Faculty_DB extends Zend_Db_Table_Abstract
which is located in application/models/ directory
I have the following in Bootstrap.php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAutoload()
{
$autoLoader = Zend_Loader_Autoloader::getInstance();
$autoLoader->registerNamespace(array('App_'));
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' =>
array('form'=>
array('path' => 'forms/',
'namespace' => 'Form_'
),
),
));
return $autoLoader;
}
}
Any clue on how to fix this?
I tried the following:
protected function _initResourceAutoloader()
{
$autoloader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => 'Application',
));
$autoloader->addResourceType( 'model', 'models', 'Model');
return $autoloader;
}
but when I do that, it tells me it can't find my Login_Form;
Given that your application namespace is Application, try:
Put your faculty DB class in application/models/Faculty.php
Name it Application_Model_Faculty
Extend it from Application_Model_DbTable_Faculty
Put the DbTable class in application/models/DbTable/Faculty.php
Since you are using Zend_Application, it will take care of setting up the autoloader and special prefixes like Form_, Model_, Plugin_ etc for you, so you can safely remove the _initAutoload and _initResourceAutoloader from your Bootstrap.
Related
So everything works fine if i use $loader->registerDirs() function, but if i use registerNamespaces function i get dispatcher error, that says: "IndexController handler class cannot be loaded". here is code for index.php file
<?php
use Phalcon\Di\FactoryDefault;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\Application;
use Phalcon\Url;
use Phalcon\Db\Adapter\Pdo\Mysql;
// Define some absolute path constants to aid in locating resources
define('BASE_PATH', dirname(__DIR__));
define('APP_PATH', BASE_PATH . '/app');
// Register an autoloader
$loader = new Loader();
//$loader->registerDirs(
// [
// APP_PATH . '/controllers/',
// APP_PATH . '/models/',
// APP_PATH . '/modules/'
// ]
//);
$loader->registerNamespaces(
[
'App\Controllers' => APP_PATH . '/controllers/'
]
);
$loader->register();
$container = new FactoryDefault();
$container->set(
'view',
function () {
$view = new View();
$view->setViewsDir(APP_PATH . '/views/');
return $view;
}
);
$container->set(
'db',
function () {
return new Mysql(
[
'host' => 'localhost',
'username' => 'sammy',
'password' => 'password',
'dbname' => 'learning',
]
);
}
);
$container->set(
'url',
function () {
$url = new Url();
$url->setBaseUri('/');
return $url;
}
);
$application = new Application($container);
// Handle the request
$response = $application->handle(
$_SERVER["REQUEST_URI"]
);
$response->send();
That my IndexController.php file.
<?php
declare(strict_types=1);
namespace App\Controllers;
use Phalcon\Http\Request;
use \Phalcon\Mvc\Controller;
use Phalcon\Http\Message\Uri;
class IndexController extends Controller
{
public function indexAction()
{
return 'hi';
}
}
so as you can see the path for registerDirs and registerNamespaces functions are the same, but it doesn't seem to work. Because of it i tried many ways of changing paths but it didn't help.
Here's my directory structure:
in short you need to change the default namespace in Phalcon\Mvc\Router
$router = $container->getRouter();
$router->setDefaultNamespace('App\Controllers');
$router->handle();
you may want to read this answer https://stackoverflow.com/a/22673713/2640796
I'm New in Phalcon framework. I'm testing my first multi module phalcon project with : Phalcon-4.0.3, i have install psr and i'm following example from phalcon documentation. everything is normal without volt engine but my backend module or controller not responding and i didnt get any error & the problem is i cant redirect to beckend module or controller if any expert please solve my issue!!
[index.php]
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Router;
use Phalcon\Mvc\Application;
$di = new FactoryDefault();
$di->set('router',function () {
$router = new Router(false);
$router->setDefaultModule('frontend');
$router->add('/backend',
[
'module' => 'backend',
'controller' => 'index',
'action' => 'index',
]
);
return $router;
}
);
$application = new Application($di);
$application->registerModules(
[
'frontend' => [
'className' => 'Multiple\Frontend\Module',//\Multiple\Frontend\Module::class,
'path' => '../apps/frontend/Module.php',
],
'backend' => [
'className' => 'Multiple\Backend\Module',//\Multiple\Backend\Module::class,
'path' => '../apps/backend/Module.php',
],
]
);
try{
$response = $application->handle($_SERVER["REQUEST_URI"]);
$response->send();
}catch (\Throwable $e) {
echo $e->getMessage();
}
[Backend Module]
namespace Multiple\Backend;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;
use Phalcon\Di\DiInterface;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\ModuleDefinitionInterface;
class Module implements ModuleDefinitionInterface
{
public function registerAutoloaders(DiInterface $di = null)
{
$loader = new Loader();
$loader->registerClasses([
'Multiple\Backend\Module' =>'../apps/backend/Module.php',
]);
$loader->registerNamespaces(
[
'Multiple\Backend\Controllers' => '../apps/backend/controllers/',
'Multiple\Backend\Models' => '../apps/backend/models/',
]
);
$loader->register();
}
public function registerServices(DiInterface $di)
{
$di->set('dispatcher',function () {
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('Multiple\Backend\Controllers');
return $dispatcher;
}
);
$di->set('view',function () {
$view = new View();
$view->setViewsDir('../apps/backend/views/');
return $view;
}
);
}
}
[Frontend Module]
namespace Multiple\Frontend;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;
use Phalcon\Di\DiInterface;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\ModuleDefinitionInterface;
class Module implements ModuleDefinitionInterface
{
/**
* Register a specific autoloader for the module
*/
public function registerAutoloaders(DiInterface $di = null)
{
$loader = new Loader();
$loader->registerClasses([
'Multiple\Frontend\Module' =>'../apps/frontend/Module.php',
]);
$loader->registerNamespaces([
'Multiple\Frontend\Controllers' => '../apps/frontend/controllers/',
'Multiple\Frontend\Models' => '../apps/frontend/models/',
]);
$loader->register();
}
/**
* Register specific services for the module
*/
public function registerServices(DiInterface $di)
{
// Registering a dispatcher
$di->set('dispatcher',function () {
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('Multiple\Frontend\Controllers');
return $dispatcher;
}
);
// Registering the view component
$di->set('view',function () {
$view = new View();
$view->setViewsDir('../apps/frontend/views/');
return $view;
}
);
}
}
I want to create a simple class that will send a predefined email to given email address. So I created the following class:
namespace Custom;
use Zend\Mail\Message;
use Zend\Mail\Transport\Smtp as SmtpTransport;
use Zend\Mail\Transport\SmtpOptions;
class Notification
{
public function sendEmailNotification($to)
{
try {
$message = new Message();
$message->addTo($to)
->addFrom('test#example.com')
->setSubject('Hello')
->setBody('Predefined email body');
$transport = new SmtpTransport();
$options = new SmtpOptions(array(
'name' => 'smtp.example.com',
'host' => 'smtp.example.com',
'port' => '587',
'connection_class' => 'plain',
'connection_config' => array(
'username' => 'test#example.com',
'password' => 'somepasswd',
'ssl' => 'tls',
),
));
$transport->setOptions($options);
$transport->send($message);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
}
From controller, the email is then sent with:
$notification = new \Custom\Notification();
$notification->sendEmailNotification('example#example.com');
This works as intended.
Next thing that I wanted to do is to move mail server configuration parameters into project configuration file (local.php). Problem is - how can I get the configuration parameters in my \Custom\Notification class (which is not a controller)?
Solutions I have found so far seem too complicated to a beginner like me. To do something like $config = $this->getServiceLocator()->get('Config'); you have to do some kind of magic all around the project.
Is there a simple way to get data from configuration file in a custom class?
You have to use injection for your purpose. Just create a factory for your controller, in which you inject the config to your controller.
namespace Application\Controller\Service;
class YourControllerFactory
{
public function __invoke(ContainerInterface $container)
{
$serviceLocator = $container->getServiceLocator();
$config = $serviceLocator->get('config');
$controller = new YourController($config);
return $controller;
}
}
For this purpose your controller needs a constructor which takes the config as parameter.
namespace Application\Controller;
class YourController extends AbstractActionController
{
protected $config;
public function __construct($config)
{
$this->config = $config;
}
public function indexAction()
{
// inject your notification class with the config
$notification = new \Custom\Notification($this->config);
$notification->sendEmailNotification('example#example.com');
}
}
Therefore your notification class needs a constructor which takes the config as parameter.
Other approaches
Another way solving your issue can be the registration of your notification class as a service. Just create a factory for your notification class, in which you create all the needed stuff and then just inject it to your notification class.
namespace Application\Mail;
class Notification
{
protected $config;
public function __construct($config)
{
$this->config = $config;
}
public function sendEmailNotification($to)
{
}
}
The factory itself is simple as pie, because we 've seen nearly the same approach in the controller factory.
namespace Application\Mail\Service;
class NotificationFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->get('config');
$notification = new Notification($config);
return $notification;
}
}
Now you just have to note it in your module.config.php file in the service manager section.
'service_manager' => [
'factories' => [
Notification::class => NotificationFactory::class,
],
],
From now on you can access the notification class with the service container of zend framework 2. Remember the factory for your controller instance shown above? Instead of injecting the config to your controller, just inject it with the notification itself. Your notification class will be created with the needed config over the notification factory.
namespace Application\Controller\Service;
class YourControllerFactory
{
public function __invoke(ContainerInterface $container)
{
$serviceLocator = $container->getServiceLocator();
$notification = $serviceLocator->get(Notification::class);
return new YourController($notification);
}
}
Have fun. ;)
I want to call the method SteeringWheelMapper->fetchCarBrandList() from a controller.
This works good now, but there's a problem.
The SteeringWheelMapper extends the AbstractWebServiceMapper which has a construct method which requires an instance of \Zend\Http\Client.
As you can see in my module.config.php file, I use "factories" for the instantiation of my SteeringWheelMapper.
The supplier has multiple products, so I will have to build multiple mappers. In the current situation that means I have to add a key to the factories config for every mapper which extends AbstractWebServiceMapper.
For example, when I want to add an ExhaustMapper, I have to add
SupplierName\Mapper\Exhaust => function ($serviceMapper) {
$httpClient => new \Zend\Http\Client;
return new SupplierName\Mapper\ExhaustMapper($httpClient);
}
Now I am repeating myself, because I also have to do this for SupplierName\Mapper\SteeringWheelMapper.
I think there should be a way to make a factory for all the mappers, instead of a new key added to the factories config.
Is my thought right?
Does anyone has a suggestion how I should do this?
Please see code below.
I'm using ZF2 and I use this setup:
/vendor
SupplierName
config
module.config.php
log
log.log
src
SupplierName
Entity
AbstractEntity.php
SteeringWheelEntity.php
Mapper
AbstractWebServiceMapper.php
SteeringWheelMapper.php
$steeringWheelMapper = $this->getServiceLocator()->get('SupplierName\Mapper\SteeringWheel');
$carBrandList = $steeringWheelMapper->fetchCarBrandsList();
SteeringWheelMapper.php
<?php
namespace SupplierName\Mapper;
class SteeringWheelMapper extends AbstractWebServiceMapper
{
public function fetchCarBrandList()
{
// Code for request
// Dispatch HTTP request
$this->dispatch();
}
}
My SupplierName/config/module.config.php looks like this:
<?php
return array(
'service_manager' => array(
'factories' => array(
'SupplierName\Mapper\SteeringWheel' => function ($serviceManager) {
$httpClient = new \Zend\Http\Client;
return new SupplierName\Mapper\SteeringWheelMapper($httpClient);
},
),
),
'supplier_name' => array(
'api' => array(
'url' => 'http://api.example.com',
),
'log' => array(
'file_location' => __DIR__ . '/../log/log.log',
),
),
);
What you're actually talking about is an abstract factory, the service manager supports the concept, but you'll need to write your own, here's an example that assumes all your mappers begin with SupplierName\Mapper
<?php
namespace SupplierName\Services;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MapperAbstractFactory implements AbstractFactoryInterface
{
public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
if (0 === strpos($requestedName, 'SupplierName\\Mapper') && class_exists($requestedName)){
return true;
}
return false;
}
public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
$httpClient = new \Zend\Http\Client;
return new $requestedName($httpClient);
}
}
In your service config, add an abstract factories key, along with the fqcn of the abstract factory, and hopefully any time you call $sm->get('SupplierName\Mapper\SomeClass'); providing the class exists, you'll get a composed instance returned
public function getServiceConfig()
{
return array(
'abstract_factories' => array(
'SupplierName\Services\MapperAbstractFactory'
),
);
}
Final working solution:
<?php
// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\I18n\Translator\Translator;
class IndexController extends AbstractActionController
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function indexAction()
{
$steeringWheelMapper = $this->getServiceLocator()->get('SupplierName\Mapper\SteeringWheel');
$carBrandList = $steeringWheelMapper->fetchCarBrandList();
return new ViewModel();
}
}
<?php
// vendor/SupplierName/src/SupplierName/Module.php
namespace SupplierName;
class Module
{
public function getConfig()
{
return include __DIR__ . '/../../config/module.config.php';
}
public function getServiceConfig()
{
return array(
'abstract_factories' => array(
'SupplierName\Mapper\MapperAbstractFactory'
),
);
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__,
),
),
);
}
}
<?php
// vendor/SupplierName/src/SupplierName/Mapper/SteeringWheelMapper.php
namespace SupplierName\Mapper;
class SteeringWheelMapper extends AbstractWebServiceMapper
{
public function fetchCarBrandList()
{
$this->dispatch();
}
}
<?php
// vendor/SupplierName/src/SupplierName/Mapper/AbstractWebServiceMapper.php
namespace SupplierName\Mapper;
use \Zend\Http\Client;
class AbstractWebServiceMapper
{
public function __construct(Client $client)
{
}
public function dispatch()
{
}
}
<?php
// vendor/SupplierName/src/SupplierName/Mapper/MapperAbstractFactory.php
namespace SupplierName\Mapper;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use \Zend\Http\Client;
class MapperAbstractFactory implements AbstractFactoryInterface
{
public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
if (0 === strpos($requestedName, 'SupplierName\Mapper')) {
$requestedName .= 'Mapper';
if (class_exists($requestedName)) {
return true;
}
}
return false;
}
public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
$requestedName .= 'Mapper';
$httpClient = new Client();
return new $requestedName($httpClient);
}
}
I'm just really starting with the Zend Framework, and currently I'm having a problem with the Zend_Loader_PluginLoader.
I managed to get a module specific plugin working easily enough using the following code:
class Api_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initPlugins()
{
$loader = new Zend_Loader_PluginLoader(array(
'Api_Plugin' => 'application/modules/api/plugins',
));
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Api_Plugin_ErrorControllerSelectorPlugin());
}
}
Edit: The class file is located at application/modules/api/plugins/ErrorControllerSelectorPlugin.php
I then tried to adapt this to get a plugin loaded for the whole application using:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAppAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'App',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
protected function _initPlugins()
{
$loader = new Zend_Loader_PluginLoader(array(
'My_Plugin' => 'application/plugins',
));
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new My_Plugin_ModuleConfigLoaderPlugin());
}
}
But I'm getting errors:
Fatal error: Class 'My_Plugin_ModuleConfigLoaderPlugin' not found in /var/www/localhost/application/Bootstrap.php on line 22
Edit: The class file is located at application/plugins/ModuleConfigLoaderPlugin.php
So - since the files are where I would expect them to be as far as the prefix/path pairs sent to Zend_Loader_PluginLoader() and the code in both cases are the same, what's the difference?
How do I get it to recognise my application-level plugins?
If you want the app-level plugin to reside within the namespace My_, you either need to put the My folder out in the library folder or declare the app-level namespace to be My_.
Assuming that you already have other stuff within your top-level app that uses the App_ namespace, then the easiest thing would be the former: move your My folder out into the library.
So, the plugin would reside in:
library/My/Plugins/ModuleConfigLoaderPlugin.php.
Then make sure that your configs/application.ini registers the My_ namespace:
autoloaderNamespaces[] = "My_"
Then the app-level Bootstrap could contain something like:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAppAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'App',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
protected function _initPlugins()
{
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new My_Plugin_ModuleConfigLoaderPlugin());
}
}
Alternatively, since your plugin does not sem to require any params, you could instantiate it via configs/application.ini using:
resources.frontcontroller.plugins[] = "My_Plugin_ModuleConfigLoaderPlugin"