yii controller filters() method pattern - filter

every controller should have method filters(), there you can specify some classes, i want to know, how those classes are include by framework ? how those classes are configuring, and when, and maybe someone can give me a pattern for this way of using filters() and including some classes ?

The controller filters are classes deriving from CFilter.
As the documentation example shows, you configure them inside your controller like this:
public function filters()
{
return array(
'postOnly + edit, create',
array(
'application.filters.PerformanceFilter - edit, create', // 1
'unit'=>'second', // 2
'amount'=>42, // 3
),
);
}
In the line marked 1 you provide the path for the hypothetical class PerformanceFilter; Yii loads that as any other component.
In the line marked 2 and 3 you initialize properties. You can go on with any number of key/value pairs; they will all get used to set the filter's properties with the corresponding names.

Related

OctoberCMS: How to use a component's functions without knowing the component's name

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

Load objects with parameters from Laravel IoC container

I need a way to load objects via IoC provider depending on a request parameter. Right now I'm loading my objects directly with App::make(xy, [$urlParamter] which I want to refactor, so that I can use dependency injection the way it is supposed to. To describe my current architecture I need to show you quiet some information and in the end you find my concrete questions I have about it.
I'm building up a general CMS framework which provides an import architecture that is extendable with a custom import implementation.
Now I'm struggling with properly loading the concrete classes via IoC container, because they always depend on the selected import.
To dig into my problem, here is my entry point in routes.php
Route::get('/import', ['as' => 'overview', 'uses' => '\CMSFramework\Http\Controllers\Import\ImportController#index']);
This generates a view where the user selects a concrete import to be triggered. After selecting a concrete import, the user should get individual views to prepare the appropriate import (i.e. Upload a CSV file, select an area to import, etc.)
In my concept an import implementation consist of:
A controller class, to implement specific (peraration-) tasks like uploading a CSV file. It inherits from a base controller of the cms framework
An import "business" or "service" class, that implements how the data is getting imported (and may further delegate to queued jobs etc.)
The CMS framework part consists of:
A base controller class for all common/shared import tasks like (start the prepared import, clean all working data, etc.)
A base service class ImportBase where all implementations inherit from. It provides an interface to receive a progress for any import and implements shared operations like cleaning up working data, etc.)
An ImportStatus class which is part of the ImportBase-Class via $ImportBase->status() to handle all runtime status informations (like "is the job still running, what is the progress). This class also provides a containter for a so called "payload" that allows any conrete import implementation to push and fetch custom status informations (ie. any sub-process has been finished)
So back to my IoC architecture. After the user selected a concrete import, the following route delegates the action to the custom import implementation's controller. If it's a framework supported standard-action like via URL /import/<importkey>/clean, the inherited BaseController of the cms framework takes over and handles the request
Route::get('/import/{key}/{method}', ['uses' => function($key, $method) {
return App::make('\\MadeleinePim\\Http\\Controllers\\Import\\'.ucfirst(camel_case($key)).'Controller')->$method($key);
}]);
I know that this direct binding via a naming convention can be improved (maybe via a custom configuration file), but for now this works for me.
Now I need to show an example of how I tried to implement a concrete import target in my controller via /import/<importkey>/seedCsvDataToDatabase:
public function seedCsvDataToDatabase($key)
{
// The IoC binding is shown in next code snippet. I did not found a good way to use method injection because
// of the route-specific parameters that control the responsible import implementation
$import = \App::make(Import::class, [$key]);
// Now trigger the import service operation of that concrete import implementation (probably bad design here)
$import->seed();
// Now, that this preparation task is done, I use the ImportStatus object which is part of the Import to store
// status informations. With this I can then decided in which step the user is (Think of it like a wizard to
// prepare any import)
$import->status()
->set(ConcreteImport::STATUS_SEEDED, true)
->set(ConcreteImport::STATUS_SEEDED_DURATION_SECONDS, (microtime(true) - $time_start) / 60);
// Back to controller method that determines in which status the import is to delegate/redirect to different
// views.
return redirect('/import/<importkey>');
}
My IoC binding for the Import class:
$this->app->singleton(Import::class, function ($app, array $parameters) {
$importKey = head($parameters);
// There is a config file that provides the class names of the concrete import implementations
$importClassName = config()->get('import.' . $importKey);
if (!$importClassName) {
throw new ImportNotFoundException($importKey, "Import with key '{$importKey}' is not setup properly'");
}
$importReflectionClass = new \ReflectionClass($importClassName);
return $importReflectionClass->newInstance($importKey);
});
And finally, the lazy loading of the import status, which is encapsulated in the ImportStatus object looks like this
public function status()
{
if (!$this->status) {
$this->status = \App::make(ImportStatus::class, [$this->key()]);
}
return $this->status;
}
I hope that demonstrates the way I try to resolve my import objects from the IoC container.
My learning so far is, that this is not the right way to inject my objects.
Is the assumption right, that I should not pass the $importKey at runtime to the App::make() and rather should try to make this independ?
My failed attempt on this was to make the IoC binding smarter and let it access the Request to properly inject my concrete import object with the required $importKey, like (pseudo code!):
$this->app->bind(ImportStatus::class, function(Container $app) {
// Did not find a good way to access the {key}-part of my route /import/{key}/{method}
$key = $app->make(Request::class)->get('key'); // Does not work like this
return new \Scoop\Import\ImportStatus($key);
});
Does this approach can work like this?
Can I somehow pass through the $importKey from my route to the ServiceProvider (or better pull it from there?)
Is there a better solution to initialize my concrete import implementations?
----------
UPDATE 1
For my lattest idea to access the Route in my IoC Binding, I got this way working:
$this->app->singleton(Import::class, function (Container $app) {
$importKey = \Route::current()->getParameter('key');
$importClassName = config()->get('import.' . $importKey);
$importReflectionClass = new \ReflectionClass($importClassName);
return $importReflectionClass->newInstance($importKey);
});
Nevertheless the idea of #Sandyandi N. dela Cruz to use a router binding prevents the direct coupling between the Binding and the Request which still doesn't feel right. Using router-binding to couple a request parameter to an implementation, sounds more appropriate.
I think you've dwelt to much on the IoC container there. Why not implement the Factory pattern and do a route binding instead of creating multiple controllers to handle different Imports? Crude example as follows:
Create a route binder - edit your app/Provider/RouteServiceProvider.php's boot() method
public function boot(Router $router)
{
parent::boot($router);
// Add the statement below.
$router->bind('import', 'App\RouteBindings#import');
}
Create the App\RouteBindings class as app/RouteBindings.php
Create an import() method with the following:
public function import($importKey, $route)
{
switch ($importKey) {
case 'import_abc':
return new ImportAbc;
break; // break; for good measure. ;)
case 'import_xyz':
return new ImportXyz;
break;
// and so on... you can add a `default` handler to throw an ImportNotFoundExeption.
}
}
Create a route for resolving an Import class.
Route::get('import/{import}/{method}', 'ImportController#handleImport');
Here, {import} will return the proper Import concrete class based on your URL.
In your ImportController's handleImport() you can do the following:
public function handleImport(Import $import, $method)
{
// $import is already a concrete class resolved in the route binding.
$import->$method();
}
So when you hit: http://example.com/import/import_abc/seed, the route binding will return a concrete class of ImportAbc and store it in $import on your handleImport() method, then your handleImport() method will execute: $import->seed();. Tip: you should probably move other controller logic such as $import->status()->set() into the Import class. Keep your controllers thin.
Just make sure your Import classes have the same signature.
It's kinda like Laravel's Route Model Binding except you create the logic for the bindings.
Again, this is just a crude example but I hope it helps.

Where to put filter form code in zf2 MVC pattern

I’ve added a small form to an index view to allow users to filter the data. I have placed the following code for the form inside the controller, but I question whether this is the right place to put it.
// ...
public function indexAction()
// ...
// build group list
$groupList = array(
0 => 'all',
1 => 'short people',
2 => 'tall people',
3 => 'fun people',
4 => 'boring people',
);
// create group selection box
$groupSelect = new Element\Select('group');
$groupSelect->setValueOptions($groupList);
$groupSelect->setAttributes(array(
'onChange' => 'this.form.submit()',
));
// create filter form
$form = new Form('group-filter');
$form->add($groupSelect);
$form->setData(array(
'group' => $group,
));
// process the form
$request = $this->getRequest();
if ($request->isPost()) {
$groupSelection = $request->getPost('group', 0);
return $this->redirect()->toRoute('admin-members', array('group'=>$groupSelection,));
}
// ...
Following the MVC pattern, does all of this code belong in the controller?
Nope it does not belong in the controller. Create a new form class (that extends Zend\Form\Form) and inject into the controller class. You can do that through the controllers factory, either through a factory class or the anonymous function "factory".
Other way to do it would be to get it (the form you created) in the controller from the service manager, but as far I know that's not the recommended method anymore, even though it still in the ZF2 docs.
That way your form code will be separated from the controller code, not mixing with the actual controller logic and, in the former case, also more easily testable.
You can learn more from this ZF2 forum thread. It's lengthy, but there are code samples and lead devs from ZF2 team are weighing in.

Laravel 4 view names in controller

I have a controller named CommentsController and 2 views, the first named searchProducts and the second named searchCategories
in the controller i used:
public function getSearchproducts() {
...
}
public function getSearchcategories() {
...
}
.... so the url generated is /comments/searchproducts and /comments/searchcategories...
i want to use urls like '/comments/serach-products' or '/comments/serach_products' is it possible using controllers ??
If your controller action contains multiple words, you may access the
action using "dash" syntax in the URI.
This is mentioned in RESTful Controllers section. So, comments/searchproducts could be used as
comments/search-products
And method would be
public function getSearchProducts() {}
Same for getSearchcategories, url could be comments/search-categories and method
public function getSearchCategories() {}
If you need to change the way your routes are built, you'll have to do them manually:
Route::get('search-products',
array(
'as' => 'search.products',
'uses' => 'CommentsController#getSearchproducts'
)
);
And then, when you do
URL::route('search-products')
It will generate a URI
yoursite/search-products
I'm not really fan of resourceful controllers, I always create them manually, and nor is Phil Sturgeon as you can see in this post.

joomla 1.5 multiple models, problem with default view

I'm developing a view that need to reuse a model, I'm following this documentation http://docs.joomla.org/Using_multiple_models_in_an_MVC_component. But that reference do the trick just (at least as far as I understand) when I use the parameter get task. if I use the view, joomla get me null data.
more clearly
controller.php - the task I named as the view I need
function viewdowhatIneed(){
$view = & $this->getView('viewdowhatIneed',html);
$view->setModel( $this->getModel( 'thenotdefaultmodelthatIneed' ), true );
$view->display();
}
model - thenotdefaultmodelthatIneed.php
class BLAModelthenotdefaultmodelthatIneed extends Jmodel{
function getReusableData0(){...}
function getReusableData1(){...}
}
view - view.html.php
class BLAViewviewdowhatIneed extends JView{
function display($tpl=null){
$dataneedit0 = $this->get('ReusableData0');
$dataneedit1 = $this->get('ReusableData1');
$this->assignRef('dataneedit0',$dataneedit0);
$this->assignRef('dataneedit1',$dataneedit1);
parent::display($tpl);
}
}
SO, what happen to me is:
example.com/index.php?option=com_BLA&view=viewdowhatIneed -> variables(datadataneedit0,dataneedit1) == NULL
example.com/index.php?option=com_BLA&task=viewdowhatIneed -> variables(datadataneedit0,dataneedit1) == Get me right data
then, my question is, is there a way to do the same thing, by using view parameter without task parameter (btw, I know this could be not a important problem, but I'm not an expert and on this reference http://docs.joomla.org/How_Joomla_pieces_work_together, it says:
The task part may or may not exist. Remember that if you omit it you are defaulting to task=display
so I really want to know that. In other words, can my view force to check the controller or vice versa.
Thanks in advance, excuse my english

Resources