I cannot seem to register a custom view helper in zend Framework 2.02 I tried all solutions posted here and anything I can think I should do but I keep getting this error:
Fatal error: Class 'ModuleName\view\Helper\mylinkhelper' not found in C:\wamp\vhosts\projectName\vendor\zendframework\zendframework\library\Zend\ServiceManager\AbstractPluginManager.php on line 177
And here's how my module.config.php looks like:
return array{
'controllers'=>array(
....
),
'view_manager' => array(
'template_path_stack' => array(
'ModuleName' => __DIR__ . '/../view',
),
),
'view_helpers' => array(
'invokables' => array(
'mylink' => 'ModuleName\view\Helper\mylinkhelper',
),
),
};
in my view file, I have:
echo $this->mylink($someparameter);
I appreciate any feedback on this. I don't really know what else to do here.
<?php
// ./module/Application/src/Application/View/Helper/AbsoluteUrl.php
namespace Application\View\Helper;
use Zend\Http\Request;
use Zend\View\Helper\AbstractHelper;
class AbsoluteUrl extends AbstractHelper
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function __invoke()
{
return $this->request->getUri()->normalize();
}
}
You’ll notice that this particular helper has a dependency — a Zend\Http\Request object. To inject this, we’ll need to set up a factory with the initialization logic for our view helper:
<?php
// ./module/Application/Module.php
namespace Application;
use Application\View\Helper\AbsoluteUrl;
class Module
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
// the array key here is the name you will call the view helper by in your view scripts
'absoluteUrl' => function($sm) {
$locator = $sm->getServiceLocator(); // $sm is the view helper manager, so we need to fetch the main service manager
return new AbsoluteUrl($locator->get('Request'));
},
),
);
}
// If copy/pasting this example, you'll also need the getAutoloaderConfig() method; I've omitted it for the sake of brevity.
}
That’s it! Now you can call your helper in your view scripts:
The full URL to the current page is: <?php echo $this->absoluteUrl(); ?>
thanks to evan to create this tutorial
It looks like the View Helper is correctly added to the ServiceManager since invoking mylink() is trying to create ModuleName\view\Helper\mylinkhelper.
I'd make sure the class is creatable with new College\view\Helper\mylinkhelper(); from a controller, this is likely to throw up some clues. Also check the filename and classname are correct.
Your approach is correct, but there might be two things which cause you this trouble:
You talk about a top level namespace ModuleName, but in your example configuration you have the top level namespace College. When you have a ModuleName namespace and you try to load College, that obviously does not work
Your view helper cannot be autoloaded. Are you sure the class name is correct (MyLinkHelper), the namespace is correct (College\View\Helper, see also above) and the file name is correct (MyLinkHelper.php). And have you enabled class-name autoloading for this module in your module class?
A third option might be the lower case "view" and "mylinkhelper" as usually you would write College\View\Helper\MyLinkHelper with a capital V, M, L and H. But since you are on Windows that should not matter afaik. I know for Linux you must be aware of case sensitiveness of class names.
The problem is that the class file is not being loaded. Its supposed to be included in autoload_classmap.php.
<?php
return array(
'{module}\View\Helper\{helper}' => __DIR__ . '\View\Helper\{helper}.php',
);
?>
I ran in the same issue and this page helped me.
As I'm new to ZF, i don't know if there is another way to add the paths in autoload_classmap, i think probably there is, but i just edited the file manually.
Got the same problem, found out by myself that view helper file was not included. while putting it into controller for testing it worked
e.g.: require_once('module/Pages/view/Helper/RenderNav.php');
why it has not been autoloaded ?
Related
In OctoberCMS, I would like to change a page process simply by attaching a different plugin component.
I have a plugin component (makeform) which inserts a form (defined in a database table).
Clicking the form's submit button calls onSubmit() which calls process().
process() is a function defined in another plugin component.
i) Can I call process() from within makeform without knowing the name of the other plugin or its component? That is, without having to use 'Acme\Plugin\Components\ProcessForm', 'processForm';
ii) Alternatively, can I programmatically discover the name of the other attached component and it's plugin, and then somehow specify it's process() function?
iii) Or is it a case of using a static properties dropdown to choose which process, and then adding the component dynamically. Always assuming I can $this->addComponent('Acme\Plugin\Components\ProcessForm', 'processForm'); beyond init().
Edit: Experimentation
I was hoping to dynamically addComponent().
Wherever I place it, in init() or elsewhere, I get the error:
Class name is not registered for the component "Acme\Plugin\Components\ProcessForm". Check the component plugin.
Even if I'm not using one of it's classes.
Many online references to this error message, but not one that has helped me.
Edit: Further explanation
A (hopefully) simplified explanation of what I'm trying to achieve.
In essence I imagine a page process consisting of a string of components.
Each component calls a function in the next component until the process ends.
The overall process can be modified simply by replacing components.
I am guessing the only way to connect components is by standardizing the function names. So this (probably?) requires components to belong to particular stages of the process, although it would be ideal if each of them can fit in at any stage (where appropriate).
illustration
extend the base component class and add your function und let the components extends from this?
make a new helper class with the function maybe call it static or so
add a global function
https://octobercms.com/forum/post/global-php-classesfunctions
I think the best approach would be to define another property in which you set the namespace of the plugin.
public function defineProperties(){
'pluginName' => [
'label' => 'Plugin Namespace to call process method from',
'type' => 'text'
]
}
--
public function onSubmit(){
$plugin = $this->property('pluginName');
$plugin::process($formData);
}
In this way you keep the component logic clean from any hardcoded plugin names.
Edit: 30/10/17
I'm not sure there's a way to list all available components inside the application. An alternative is to set up a Settings page, with a repeater of sorts in which you declare all available components with namespaces.
You than parse this to an array inside the onSubmit method and return this to the dropdown.
public function defineProperties(){
'components' => [
'label' => 'Plugin Namespace to call process method from',
'type' => 'dropdown',
'options' => 'getComponentsOptions' // optional but easier to understand
]
}
public function getComponentsOptions(){
$components = Settings::get('components');
$options = [];
foreach ($components as $component)
{
$options[$component['namespace']] = $component['name'];
}
return $options;
}
/Models/Settings/fields.yaml
fields:
components:
type: repeater
form:
fields:
name:
placeholder: My Component Name
span: left
namespace:
placeholder: Acme\Name\Components\MyComponent;
span: right
/Models/Settings.php
class Settings extends Model
{
public $implement = ['System.Behaviors.SettingsModel'];
// A unique code
public $settingsCode = 'acme_name_settings';
// Reference to field configuration
public $settingsFields = 'fields.yaml';
}
http://octobercms.com/docs/plugin/settings#introduction
I have an associative array which I use in approx all controllers and i was wondering if possible to define that array somewhere at one place and just use in in all controllers?
Kind of like we do in angular.
Like if i can define it in env file or something.
Please let me know if there is a way.
Creating an entry in the config/app.php
'myVar' => [
'key' => 'value'
],
and accesing it via config('app.myVar')
Put it in a helper file and access using that helper file
check out this answer https://stackoverflow.com/a/32772686/5808894
Using AppServiceProvider
In your app/providers/AppServiceProvider.php in the boot method add this, make sure to import App use App in AppServiceProvider
public function boot()
{
App::singleton('myVar', function(){
return [
'key' => 'value'
];
});
}
and access the variable in your controller using app('myVar');
Reference https://stackoverflow.com/a/25190686/5808894
Mentioning it here since no one else did:
public class SharedArrayContainer {
public static $data = [ 'key' => 'value' ];
}
and you can use it as:
SharedArrayContainer::$data
Not as good as adding it to the service container but this is what pre-framework me used to do.
I would recommend to create a provider class (service) for it and use laravel Service Container for injection. This way you can create helper methods like get, find, etc... and make use of laravels Dependency Injection (having single instance injected whenever & almost everywhere you want)
Laravel docs
class ExampelService
{
// associative array
private arr = []
public function get(item) { }
public function save(item) { }
public function has(item) { }
}
I want to convert a URL which is of the format
path/to/my/app/Controller_action/id/2
to
path/to/my/app/Controller_action/id/User_corresponding_to_id_2
I have already seen this tutorial from Yii, but it isnt helping me with anything. Can anyone help me with this?
EDIT: I would also like to know if this thing is even possible in the POST scenario, ie I will only have path/to/my/app/Controller_action in the URL.
Add a getUrl method in your User model
public function getUrl()
{
return Yii::app()->createUrl('controller/action', array(
'id'=>$this->id,
'username'=>$this->username,
));
}
Add the following rule urlManager component in config/main.php
'controller/action/<username:.*?>/<id: \d+>'=>'controller/action'
And use the models url virtual attribute everywhere
dInGd0nG is on the correct track, but if I understand correctly you wish to do actions based on the actual username instead of the ID as well right?
It's not that hard in Yii. I'm assuming here for simplicity the controller is user and the action is view.
Your User controller:
public function actionView($id)
{
if (is_numeric($id))
$oUser = User::model()->findByPk($id);
else
// Luckily Yii does parameter binding, wouldn't be such a good idea otherwise :)
$oUser = User::model()->findByAttributes(array('username' => $id));
...
}
Your urlManager config:
'user/view/<id: \w+>' => 'user/view',
Or more generally:
'user/<action: \w+>/<id: \w+> => 'user/<action>',
To generate a user url in a view:
$this->createUrl('user/view', array('id' => $oUser->username));
I used the skeleton zf2 application from github
here is an excerpt from the ZF2 reference that should get you started:
14.4.2. Registering Helpers Zend\View\Renderer\PhpRenderer composes a plugin broker for managing helpers, specifically an instance of
Zend\View\HelperBroker, which extends the base plugin broker in order
to ensure we have valid helpers available. The HelperBroker by default
uses Zend\View\HelperLoader as its helper locator. The HelperLoader is
a map-based loader, which means that you will simply map the
helper/plugin name by which you wish to refer to it to the actual
class name of the helper/plugin.
Programmatically, this is done as follows:
// $view is an instance of PhpRenderer
$broker = $view->getBroker();
$loader = $broker->getClassLoader();
// Register singly:
$loader->registerPlugin('lowercase', 'My\Helper\LowerCase');
// Register several:
$loader->registerPlugins(array(
'lowercase' => 'My\Helper\LowerCase',
'uppercase' => 'My\Helper\UpperCase',
));
Within an MVC application, you will typically simply pass a map of
plugins to the class via your configuration.
// From within a configuration file
return array(
'di' => array('instance' => array(
'Zend\View\HelperLoader' => array('parameters' => array(
'map' => array(
'lowercase' => 'My\Helper\LowerCase',
'uppercase' => 'My\Helper\UpperCase',
),
)),
)),
);
The above can be done in each module that needs to register helpers
with the PhpRenderer; however, be aware that another module can
register helpers with the same name, so order of modules can impact
which helper class will actually be registered!
14.4.3. Writing Custom Helpers Writing custom helpers is easy. We recommend extending Zend\View\Helper\AbstractHelper, but at the
minimum, you need only implement the Zend\View\Helper interface:
namespace Zend\View;
interface Helper
{
/**
* Set the View object
*
* #param Renderer $view
* #return Helper
*/
public function setView(Renderer $view);
/**
* Get the View object
*
* #return Renderer
*/
public function getView();
}
If you want your helper to be capable of being invoked as if it were a
method call of the PhpRenderer, you should also implement an
__invoke() method within your helper.
As previously noted, we recommend extending
Zend\View\Helper\AbstractHelper, as it implements the methods defined
in Helper, giving you a headstart in your development.
Once you have defined your helper class, make sure you can autoload
it, and then register it with the plugin broker.
Here is an example helper, which we're titling "SpecialPurpose"
namespace My\View\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');
}
}
Then assume that when we register it with the plugin broker, we map it
to the string "specialpurpose".
Within a view script, you can call the SpecialPurpose helper as many
times as you like; it will be instantiated once, and then it persists
for the life of that PhpRenderer instance.
// remember, in a view script, $this refers to the Zend_View instance.
echo $this->specialPurpose();
echo $this->specialPurpose();
echo $this->specialPurpose();
The output would look something like this:
I have seen 'The Jerk' 1 time(s). I have seen 'The Jerk' 2 time(s). I
have seen 'The Jerk' 3 time(s).
I want to separate certain functions from controller methods into libraries to make them modular. Based on the URI I'm trying to route to a library method if it exists. But I'd have to load the library to check for the method with the php method_exists function. The only other way I've found to check for it is to put all the library methods in a config array and check for the method name there. Such as:
<?php
/**
* Application_config.php
*/
$config['extensions'] = array(
'News' => array(
'library' => 'articles_library',
'methods' => array(
'articles',
'article',
'edit_article',
'add_article',
'delete_article'
)
)
);
and
<?php
/**
* admin_controller.php
*
* all admin routes go to index
*/
class C3_base_controller extends Controller {
public function index() {
$lib_chk = 0;
$ext = $this->config->item('extensions');
foreach($ext as $item) {
foreach ($item['methods'] as $meth) {
if ($this->uri->segment(2) == $meth) {
$lib = $item['library'];
echo $this->$lib->$meth();
$lib_chk = 1;
}
}
}
if ($lib_chk == 0) {
// rest of controller...
}
}
}
Is there a better way to check for the existence of a library method in each library without loading the libraries? Having to duplicate every method in the application config is just asking for it.
You can use
method_exists
For example, if you are checking for the database object if it's exists
if(method_exists($this->CI->db, 'set')){
// code ...
}
Are you pursuing modularity for reusable code or are you creating an application that has features that may or may not be present...as in plugins?
It's hard for me to imagine your base application having hard-coaded url's that lead to a library that may not exist. Can you test for the library rather than the method? That may be simpler to check a directory and load file names into an array, then see if the library required by a url or uri is in that array.
But if you need to stick to your array of existing method names, could you just regex for the method names by that occur after "function (space)" and followed by parenthesis?