I'm using the default framework code that was created with the Zend Framework Application tool, I added some autoloader magic so that any classes named Default_<*>_<*> would automatically be loaded from the correct directory, in this case Default_Model_TableName.
application/models/ProjectCategories.php:
<?php
class Default_Model_ProjectCategories extends Zend_Db_Table_Abstract {
protected $_name = 'categories';
protected $_dependentTables = array('Projects');
}
application/models/Projects.php:
<?php
class Default_Model_Projects extends Zend_Db_Table_Abstract {
protected $_name = 'projects';
protected $_referenceMap = array(
'Category' => array(
'columns' => 'cid',
'refTableClass' => 'ProjectCategories',
'refColumns' => 'id',
'onUpdate' => self::CASCADE,
'onDelete' => self::CASCADE,
)
);
}
What I am attempting to do is the following:
<?php
$categories = new Default_Model_ProjectCategories();
$category = $categories->find('1');
$category->findProjects();
At which point I get an error thrown at me that it is unable to find Projects.php, and or that the file might not have contained a class named Projects.
At that point I place Projects.php in the include path that was set up by the framework (/../library/) and the file is found, but now I lose my whole directory structure, and naming because I had to rename Default_Model_Projects to Projects. I am able to get everything to work if I place the file back in its original location, and change
protected $_dependentTables = array('Projects');
to
protected $_dependentTables = array('Default_Model_Projects');
but this also means that my ->findProjects() now becomes ->findDefault_Model_Projects().
Is there a way to tell it that when I am looking for findProjects() that it has to instantiate Default_Model_Projects? Is this something that is missing from Zend Framework, or am I attempting to shoehorn something in a way that it does not belong? How have you solved this issue?
This problem may have been introduced by the new Autoloader, and the way in which it prefers to load namespaced classes (e.g. those prefixed as you have ).
In my applications I simply name models like 'Projects' and add a separate models folder to the include path for that application. This is one solution - unfortunately I don't know how you can make the namespaced models load correctly, but I would suggest looking at Zend_Loader in greater detail, and possible the pluginLoaders.
i used to be able to do something like
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => 'App',
));
$resourceLoader->addResourceType('model', 'models/', '');
to shrink my model classes to App_TableName but seems like its not working now...
Change
protected $_dependentTables = array('Projects');
to
protected $_dependentTables = array('Default_Model_Projects');
Related
I have a Yii 1.x component loaded in the configuration file like so
$config['components']['simplesamlphp'] = array(
'class' => 'application.components.yii-simplesamlphp.components.Simplesamlphp',
'autoloadPath' => SAML_DIR.'/test2/lib/_autoload.php',
'authSource' => 'default-sp',
);
I need to make the autoloadPath property dynamic based on who the user is in the controller. Is this possible? And if so how do I overwrite it?
Probably the best way is to extend Simplesamlphp and configure property in init():
class MySimplesamlphp extends Simplesamlphp {
public $adminAutoloadPath;
public $nonAdminAutoloadPath;
public function init() {
if (Yii::app()->user->isAdmin()) {
$this->autoloadPath = $this->adminAutoloadPath;
} else {
$this->autoloadPath = $this->nonAdminAutoloadPath;
}
parent::init();
}
}
And use new component in config:
$config['components']['simplesamlphp'] = array(
'class' => 'MySimplesamlphp',
'adminAutoloadPath' => SAML_DIR.'/test2-admin/lib/_autoload.php',
'nonAdminAutoloadPath' => SAML_DIR.'/test2/lib/_autoload.php',
'authSource' => 'default-sp',
);
I figured it out overriding yii components is fairly easy even if you dont initialize it in the config.
$component = array(
'class' => 'application.components.yii-simplesamlphp.components.Simplesamlphp',
'autoloadPath' => SAML_DIR.'/'.$tenant_path.'/lib/_autoload.php',
'authSource' => 'default-sp',
); //where $tenant_path is the directory of the component i need based on the tenant
Yii::app()->setComponent('simplesamlphp',$component);
then use the component in your controller like so
Yii::app()->simplesamlphp;
Note that you will only have access to the component within your controller method so all i did was move the that code to its own class and call it when i needed to create a new instance of the component
I created new resources with this code:
class WebserviceRequest extends WebserviceRequestCore {
public static function getResources(){
$resources = parent::getResources();
// if you do not have class for your table
$resources['test'] = array('description' => 'Manage My API', 'specific_management' => true);
$resources['categoryecommerce'] = array('description' => 'o jacie marcin', 'class' => 'CategoryEcommerce');
$mp_resource = Hook::exec('addMobikulResources', array('resources' => $resources), null, true, false);
if (is_array($mp_resource) && count($mp_resource)) {
foreach ($mp_resource as $new_resources) {
if (is_array($new_resources) && count($new_resources)) {
$resources = array_merge($resources, $new_resources);
}
}
}
ksort($resources);
return $resources;
}
}
And new class:
class CategoryEcommerceCore extends ObjectModelCore {
public $category_id;
public $category_core_id;
public static $definition = array(
'table' => "category_ecommerce",
'primary' => 'category_id',
'fields' => array(
'category_core_id' => array('type' => self::TYPE_INT),
)
);
protected $webserviceParameters = array();
}
Webservice is override properly. My class WebserviceRequest is copying to
/override/classes/webservice/WebserviceRequest
but class isn't copying to /override/classes/ when i installing my module.
How to add new resourcess with own logic ? I want to add categories within relation to my table.
Regards
Martin
As soon as there is literally nothing regarding the API except Webkul tutorial... I tried to implement the "Webkul's" tutorial, but also failed. However seems that it's better to use hooks instead of overrides. I used my "reverse engineering skills" to determine the way to create that API, so-o-o-o, BEHOLD! :D
Let's assume you have a custom PrestaShop 1.7 module. Your file is mymodule.php and here are several steps.
This is an install method wich allows you to register the hook within database (you can uninstall and reinstall the module for this method to be executed):
public function install() {
parent::install();
$this->registerHook('addWebserviceResources');
return true;
}
Add the hook listener:
public function hookAddWebserviceResources($resources) {
$added_resources['test'] = [
'description' => 'Test',
'specific_management' => true,
];
return $added_resources;
}
That specific_management option shows you are going to use WebsiteSpecificManagement file instead of database model file.
Create WebsiteSpecificManagement file, called WebsiteSpecificManagementTest (Test - is CamelCased name of your endpoint). You can take the skeleton for this file from /classes/webservice/WebserviceSpecificManagementSearch.php. Remove everything except:
setObjectOutput
setWsObject
getWsObject
getObjectOutput
setUrlSegment
getUrlSegment
getContent (should return $this->output; and nothing more)
manage - you should rewrite it to return/process the data you want.
Add
include_once(_PS_MODULE_DIR_.'YOURMODULENAME/classes/WebserviceSpecificManagementTest.php');
to your module file (haven't figured out how to include automatically).
Go to /Backoffice/index.php?controller=AdminWebservice and setup the new "Auth" key for your application, selecting the test endpoint from the permissions list. Remember the key.
Visit /api/test?ws_key=YOUR_KEY_GENERATED_ON_STEP_4 and see the XML response.
Add &output_format=JSON to your URL to see the response in JSON.
You have to use something like $this->output = json_encode(['blah' => 'world']) within manage method at WebsiteSpecificManagementTest.
NOTE: This is an old question and the answers here no longer works (since beta5). See this question on how to do it with ZF2 stable version.
I have looked at this example from the manual. Note that this is version 2 of the Zend Framework.
I create this helper:
<?php
namespace Mats\Helper;
use Zend\View\Helper\AbstractHelper;
class SpecialPurpose extends AbstractHelper
{
protected $count = 0;
public function __invoke()
{
$this->count++;
$output = sprintf("I have seen 'The Jerk' %d time(s).", $this->count);
return htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
}
}
?>
and then try to register it like this:
return array(
'di' => array('instance' => array(
'Zend\View\HelperLoader' => array('parameters' => array(
'map' => array(
'specialpurpose' => 'Mats\Helper\SpecialPurpose',
),
)),
)),
);
but when doing this in a view, for instance add.phtml
<?php echo $this->specialPurpose(); ?>
It will crash, saying it cannot find the helper.
However, in the same add.phtml file I can do
<?php $helper = new Mats\Helper\SpecialPurpose(); ?>
and have access to it, so I guess the namespace should be right?
Currently I register it in Module.php, but I have also tried elsewhere.
My goal is to have access to the view helper in all views in my module, without having to create an instance of it in each phtml file, and not having to add it every time in the controller.
How can this be achieved? Thanks.
ZF2 moved to service managers with programmatic factories, while di used as fallback factory.
For view there is view manager now and as service resolution stops as soon as it found factory, helpers configured via di no longer work.
Example how you should register helpers now you can find in ZfcUser module config
Add custom helper is very simple, just add one line to your module config file like this:
return array(
'view_manager' => array(
'helper_map' => array(
'specialPurpose' => 'Mats\Helper\SpecialPurpose',
),
),
);
How I can get access to my module config from the controller?
I am really surprised at how obscure this is, because I had exactly the same problem and could not find a definitive answer. One would think the ZF2 documentation would say something about this. Anyhow, using trial and error, I came across this extremely simple answer:
Inside controller functions:
$config = $this->getServiceLocator()->get('Config');
Inside Module class functions (the Module.php file):
$config = $e->getApplication()->getServiceManager()->get('Config');
whereas $e is an instance of Zend\Mvc\MvcEvent
In general, the config is accessible from anywhere you have access to the global service manager since the config array is registered as a service named Config. (Note the uppercase C.)
This returns an array of the union of application.config.php (global and local) and your module.config.php. You can then access the array elements as you need to.
Even though the OP is quite old now, I hope this saves someone the hour or more it took me to get to this answer.
What exactly do you want to do in your controller with the module configuration? Is it something that can't be done by having the DI container inject a fully configured object into your controller instead?
For example, Rob Allen's Getting Started with Zend Framework 2 gives this example of injecting a configured Zend\Db\Table instance into a controller:
return array(
'di' => array(
'instance' => array(
'alias' => array(
'album' => 'Album\Controller\AlbumController',
),
'Album\Controller\AlbumController' => array(
'parameters' => array(
'albumTable' => 'Album\Model\AlbumTable',
),
),
'Album\Model\AlbumTable' => array(
'parameters' => array(
'config' => 'Zend\Db\Adapter\Mysqli',
)),
'Zend\Db\Adapter\Mysqli' => array(
'parameters' => array(
'config' => array(
'host' => 'localhost',
'username' => 'rob',
'password' => '123456',
'dbname' => 'zf2tutorial',
),
),
),
...
If you need to do additional initialization after the application has been fully bootstrapped, you could attach an init method to the bootstrap event, in your Module class. A blog post by Matthew Weier O'Phinney gives this example:
use Zend\EventManager\StaticEventManager,
Zend\Module\Manager as ModuleManager
class Module
{
public function init(ModuleManager $manager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'doMoarInit'));
}
public function doMoarInit($e)
{
$application = $e->getParam('application');
$modules = $e->getParam('modules');
$locator = $application->getLocator();
$router = $application->getRouter();
$config = $modules->getMergedConfig();
// do something with the above!
}
}
Would either of these approaches do the trick?
for Beta5, you can add function like this in Module.php
public function init(ModuleManager $moduleManager)
{
$sharedEvents = $moduleManager->getEventManager()->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
$config = $e->getApplication()->getConfiguration();
$controller = $e->getTarget();
$controller->config = $config;
});
}
in controller, you can get config :
print_r($this->config);
To read module-only config your module should just implement LocatorRegisteredInterface
Before:
namespace Application;
class Module
{
// ...
}
After:
namespace Application;
use Zend\ModuleManager\Feature\LocatorRegisteredInterface;
class Module implements LocatorRegisteredInterface
{
// ...
}
That implementation says LocatorRegistrationListener to save module intance in service locator as namespace\Module
Then anywhere you can get access to your module:
class IndexController extends AbstractActionController
{
public function indexAction()
{
/** #var \Application\Module $module */
$module = $this->getServiceLocator()->get('Application\Module');
$moduleOnlyConfig = $module->getConfig();
// ...
}
}
There is a pull request ready now which pulls the module class (so the modules/foo/Module.php Foo\Module class) from the DI container. This gives several advantages, but you are also able to grab that module instance another time if you have access to the Zend\Di\Locator.
If your action controller extends the Zend\Mvc\Controller\ActionController, then your controller is LocatorAware. Meaning, upon instantiation your controller is injected with the locator knowing about modules. So, you can pull the module class from the DIC in your controller. Now, when your module consumes a config file and stores this inside the module class instance, you can create a getter to access that config data from any class with a locator. You probably have already an accessor with your module Foo\Module::getConfig()
While ZF2 is heavily under development and perhaps this code will change later on, this feature is currently covered by this test, with this the most relevant part:
$sharedInstance = $locator->instanceManager()->getSharedInstance('ListenerTestModule\Module');
$this->assertInstanceOf('ListenerTestModule\Module', $sharedInstance);
So with $sharedInstance your module class, you can access the config from there. I expect a shorthand for this feature soon, but this can only be done after PR #786 has been merged in ZF2 master.
You need to implements ServiceLocatorAwareInterface from your model. And then you can set setServiceLocator() and getServiceLocator() which give you direct access to the service manager. Take a look at this code sample https://gist.github.com/ppeiris/7308289
I created the module with controller plugin and view helper for reading a config in controllers and views. GitHub link __ Composer link
Install it via composer
composer require tasmaniski/zf2-config-helper
Register new module "ConfigHelper" in your config/application.config.php file
'modules' => array(
'...',
'ConfigHelper'
),
Use it in controller and view files
echo $this->configHelp('key_from_config'); // read specific key from config
$config = $this->configHelp(); // return config object Zend\Config\Config
echo $config->key_from_config;
you can also access any config value anywhere by this hack/tricks
$configReader = new ConfigReader();
$configData = $configReader->fromFile('./config.ini');
$config = new Config($configData, true);
I have a test module. In test module I have a Form in forms folder.
myproject/application/modules/test/forms/TestForm.php
class Test_Form_TestForm extends Zend_Form {
//form elements
}
myproject/application/modules/test/controllers/TestController.php
class Test_TestController extends Zend_Controller_Action {
public function indexAction() {
$this->view->form = new Test_Form_TestForm(); // this is generating error
}
} // end class
Form initialization in controller is generating following error:
Fatal error: Class 'Test_Form_TestForm' not found in C:\wamp\www\student\application\modules\notification\controllers\NotificationController.php on line 16
How to make this form accessable in controller. Same type of case is working with default controller. I know I have to register my module in bootstrap with Form_ indicator but dont know exact syntax.
You can also initialize multiple modules in a separate function in one Bootstrap file like:
protected function _initAutoloaders() {
$test_loader = new Zend_Application_Module_Autoloader( array( 'namespace' => 'Test',
'basePath' => APPLICATION_PATH . '/modules/test'
));
$mynew_loader = new Zend_Application_Module_Autoloader( array( 'namespace' => 'Mynew',
'basePath' => APPLICATION_PATH . '/modules/mynew'
));
}
In order for Zend Autoloader to work for your modules, you need to have bootstraps for all of your modules, and also modules resource initialized.
So, in your application/modules/test/Bootstrap.php:
class Test_Bootstrap extends Zend_Application_Module_Bootstrap {}
Upd:
And in your application/configs/application.ini:
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] =
More info about autoloading in modules here
Vika's answer is correct on how to setup modules autoloader.
Your error states that the form class cannot be found in notification module under NotificationController controller.
So you need to have bootstrap class for the notification module
In your application/modules/notification/Bootstrap.php:
class Notification_Bootstrap extends Zend_Application_Module_Bootstrap {}
I don't know if this is the best way, but it works.
In your bootstrap
...
$autoloader = new Zend_Loader_Autoloader_Resource(array('namespace' => '', 'basePath' => APPLICATION_PATH));
$autoloader->addResourceType('Test_Form', '/test/forms', 'Test_Form');
...
Vika's answer seems to be correct.
If you still having problems, try modify your application.ini
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.frontController.moduleDefault = "test"
resources.modules[] = "test"
resources.modules[] = "other"
If you specify exact module names in resource list, Zend will auto-magically register the Form and other resource auto-loaders. In debugging case modules/test/Boostrap.php should be triggered and any _init method inside. Have fun.