This thread takes of from where the massive skeleton thread becomes a discussion of ApplicationController rather than the FrontController. About right here.
I agree.Quote:
Originally Posted by Ryan Wray
Printable View
This thread takes of from where the massive skeleton thread becomes a discussion of ApplicationController rather than the FrontController. About right here.
I agree.Quote:
Originally Posted by Ryan Wray
Someone should define first what an application controller is since it can mean different things.
I myself consider the application controller just a front controller with a filter in it for handling authentification for example.
From my observation of the last thread, the lastest file realease (made by arborint) is available here.
kyberfabrikken has given a good account of things that need to be thought about when it comes to the Application Controller.
From now on, hopefully we can sustain this thread independtly from the Front Controller. Alot of information is available about the Front Controller, including development details and code, in this thread
Now, lets make this thread just as big as the other one! :)
Despite the flexibility we attained in the first part, I think it's pretty important for people contributing to the ApplicationController to know a bit about our FrontController design, so as to take advantage of what we already have and avoid overlapping responsibilities - here's a wrap up:Conclusive posts by me here (PHP5) and by arborint here (PHP4), both equipped with functioning code packages.Quote:
Originally Posted by Ezku
As for this thread, up first we'd need a solid definition on what an ApplicationController is and what our goals are.
I pretty much agree on that - so it's probably a matter of using the right word for it. How about FormDescriptor instead ? This object would hold a list of which fields the form should have, aswell as their rules and filters. Based on this FormDescriptor, we could create a FormView, which renders the html-markup for the form, and a FormSubmitHandler which would update the form from the request. Dependning on if the form is valid, the FormSubmitHandler would then either redirect back to the FormView (on not valid) or execute a domain-command (on valid). I suppose this makes the FormSubmitHandler an ApplicationController ?Quote:
Originally Posted by arborint
I have stitched some code together for this, to get us started. The implementation of FormView and FormSubmitHandler are very sketchy - especially the latter could probably lead to some discussion.
I have re-arranged Filters and Rules a bit ... the biggest change is probably that I introduced a Logger, rather than let the Rule return messages. This is a design I have used before, and I quite like it. What do you think about this ?
Looking at the UML it seems very nice. Good job.Quote:
Originally Posted by kyberfabrikken
Very briefly before I go,
I think the idea of using a Logger is an excellent approach. Will try to look at your scripts before the weekend though :)Quote:
What do you think about this ?
Just looked briefly at your code. I must say I'm not too wild about having PHP echo HTML-contents. In your view.php for example, you print a bunch of textfields. Why not have these textfields in a template, or perhaps serverpage instead?
Btw, have you looked at the latest revision of the code in the Front Controller thread? (available here)
That's what I meant when I wrote that they were very sketchy. FormView could be implemented as a serverpage or as a widget, and would use a widget for each fieldtype. Likewise FormSubmitHandler should likely be implemented as a class implementing IHandler.Quote:
Originally Posted by Overunner
A design-issue to consider, which I didn't mention is the FilterChain and Validator classes. There really isn't any reason why they are seperate classes, when they could just be incorporated directly into FieldDescriptor. What's the general feeling towards this ?
I'd be interested in seeing a sketch on this later.Quote:
Originally Posted by kyberfabrikken
I don't see a reason for them to be separate. Go ahead.Quote:
There really isn't any reason why they are seperate classes, when they could just be incorporated directly into FieldDescriptor. What's the general feeling towards this ?
You're right, this is AC functionality. Let's keep it at that - the input validation stuff you're now designing is something for the AC or its delegates to be used. By the way, do they have to be named "Form" when there's no real form functionality, it's just input validation as it is - I suppose the purpose was to create an InputController, which may or may not be different from what you have right now. I hope I'm making sense. :DQuote:
Originally Posted by kyberfabrikken
I really don't see the difference between the code you posted and what Overrunner and I had discussed as a Form Controller except I think Overrunner's rules are superior. It is more a stylistic difference with a Logger and Factory added. I don't know why you'd need a Factory when 99% of the time there will just be one form. I am not a big fan of Loggers because they are just fancy echo() statements and with unit tests they are not really needed. I think if you wanted to tuck the error messages into an object then using the Datasource class would be the most straightforward. But what you really have is a Context object.Quote:
Originally Posted by kyberfabrikken
If we extend the InputController class to create a FormController class it the same as your "FormDescriptor" and if we extend the InputController class to create a FormControllerParameter class it the same as your "FieldDescriptor". Inheritence is not the only way to deal with this. We could also have a context object to move the information between them. It would be good to support both methods.
I notice that because managing forms is so common that monolithic solutions are very common. The thing I have been trying to communicate is that a Form Contoller is really a three state Application Controller. Whereas an Application Controller can deal with N states and can extend across many pages, a Form Contoller really only deals with a single page (or two pages) and has the states: init, redisplay and done. Those three states are built from the following capabilities: determine state, load data, display form, filter/validate, save data. If load/save are the Model and display form is the View, then that puts determine state and filter/validate in the Controller. So far we have filter/validate but we haven't dealt with "determine state".
Unfortunately the "determine state" part of a From Controller is really just a couple of IF statements (i.e. Form Submitted? No Errors?). That is part of what makes the leap to an Application Controller difficult.
That get's me back to the Input Controller because it isn't done yet (as McGruff noted it is just a Request Processer right now). How to we add the state logic and possibly dispatch to the Input Controller? I am beginning to think that plug-in state machines (similar in concept/goals the plug-in Mappers in the Front Controller) is the way to go. That decouples the logic of the page moving it out of the housekeeping and structure code. The goal is that you can write a Controller with any behavior by changing just the state machine with the goal of not having the maximum amount of code outside the state machine. We could supply standard single form and wizard (multi-form sequence) state machines (and the requsite RoR emulator ;)).
Really good post there arborint - it made a few pieces come together for me.
The thought was that the factory would load a different form from a config-file, that the programmer could supply. I probably didn't make that very clear though.Quote:
Originally Posted by arborint
What do you mean by not needed ? Anyway ... I kind of like the logger-class. One could for example use a Logger, that logs to $_SESSION, so that the messages were available at next request.Quote:
Originally Posted by arborint
What I don't like on the other hand is when a method "returns" data by setting a property. Like how the rules return their errormessages in the overunner-code.
What do you mean by context object ?Quote:
Originally Posted by arborint
Right. Let's do that then.Quote:
Originally Posted by arborint
That sounds very appealing.Quote:
Originally Posted by arborint
Off Topic:
Whichever way it turns out, I must insist though to keep the interface IRule for obvious reasons.
Although I initially liked the Factory, after giving it some thought I must question the need for it. The configuration part is also a bit on the fuzzy side... Perhaps I like the idea but dislike the execution. Whereas the Logger I do like - it's just that you can do it with a regular Datasource, no need for a specialized class. :) If you need persistance, use an extended PersistentDatasource.Quote:
Originally Posted by arborint
I'm a bit confused - are you creating a FormController or an InputController? The naming, of course, implies the former, but there's nothing form-specific as far as I can tell. Was I right in saying that whichever is for the AC to use?
As well as the IFilter.Quote:
Originally Posted by kyberfabrikken
I think I was just firing blind. As arborint points out, much of it was just the InputController0.4 code with different naming. I didn't realize it till now.Quote:
Originally Posted by Ezku
As for the DataSource class ... what is that really for ? I can't see it used anywhere in the code, and seems to be such an abstract concept that it bears no real meaning ?
By the way - Why use arrays for rules/filters in the InputControllerParameter class ? Wouldn't the following be easier and a lot cleaner ?
I also noticed that the filterRequest method of the class InputController modifies the original request. I don't think that's a good idea. Besides - the method-name is a bit deceptive. It should probably be named import or something similar ?PHP Code:class InputControllerParameter
{
var $name = '';
var $value = '';
var $filters;
var $rules;
function InputControllerParameter($name) {
$this->name = $name;
$this->filters =& new FilterChain();
$this->rules =& new Validator();
}
function addFilter(&$filter) {
$this->filters->addFilter($filter);
}
function addRule(&$rule) {
$this->rules->addRule($rule);
}
function getValue() {
return $this->value;
}
function setValue($value) {
$this->value = $value;
}
} // end class InputControllerParameter
How about :
PHP Code:class InputController
{
(...)
function import(&$request) {
foreach (array_keys($this->params) as $key) {
$this->params[$key]->setValue($this->params[$key]->filters->doFilter($request->get($key)));
}
}
(...)
} // end class InputController
Yes. But say - why would one want the InputControllerParameter to keep track of its filters and rules if it's basically the InputController's job, and they [filters & rules] should be able to access all data anyway (in order to have combined requirements)? I'm asking because everyone seems to be doing it and I don't know why.Quote:
Originally Posted by kyberfabrikken
Yeah, that had me puzzled for a while as well. The fact is it's just an object encapsulating an array. :)Quote:
As for the DataSource class ... what is that really for ? I can't see it used anywhere in the code, and seems to be such an abstract concept that it bears no real meaning ?
I guess the point in this context was that the filters and rules, hosted by the InputController, would get passed a Datasource (my DataObject) containing all the data so as to let them access whatever fields they want.
I use this myself (PHP5):There's some basic functionality here that comes in handy in a multitude of objects, say Request.PHP Code:<?php
/**
* Name: DataObject.php
* Purpose: Generic data holder object. Provides ArrayAccess and Iterator functionality.
*/
class DataObject implements Iterator, ArrayAccess
{
protected $data = array();
/**
* Set a parameter.
* @param string parameter name
* @param mixed parameter value
*/
public function set($name, $value)
{
$this->data[$name] = $value;
}
/**
* Retrieve a parameter.
* @param string parameter name
* @param mixed default value if not found
*/
public function get($name, $default=NULL)
{
return (isset($this->data[$name]) ? $this->data[$name] : $default);
}
/**
* Indicate whether or not a parameter exists.
* @param string parameter name
* @return boolean
*/
public function has($name)
{
return isset($this->data[$name]);
}
/**
* Remove a parameter.
* @param string parameter name
*/
public function remove($name)
{
unset($this->data[$name]);
}
/**
* Retrieve parameter names.
* @return array
*/
public function names()
{
return array_keys($this->data);
}
/**
* Retrieve all data
* @return array object data
*/
public function all()
{
return $this->data;
}
/**
* Replicate data (any old parameters are lost)
* @param mixed Array or ArrayAccess
*/
public function copy($data)
{
if (is_array($data) || $data instanceof ArrayAccess)
{
$this->data = $data;
reset($this->data);
}
}
/**
* Append parameter.
* @param mixed parameter value
*/
public function append($value)
{
$this->data[] = $value;
}
/**
* DataObject constructor. Pass a copyable argument to set initial values.
* @param mixed initial values, optional
*/
public function __construct($data = NULL)
{
$this->copy($data);
}
public function __set($name, $value) { return $this->set($name, $value); }
public function __get($name) { return $this->get($name); }
/**
* Iterator implementation
*/
public function current() { return current($this->data); }
public function key() { return key($this->data); }
public function next() { return next($this->data); }
public function rewind() { return reset($this->data); }
public function valid() { return current($this->data) !== FALSE; }
/**
* ArrayAccess implementation
*/
public function offsetExists($offset) { return isset($this->data[$offset]); }
public function offsetSet($offset, $value) { $this->set($offset, $value); }
public function offsetGet($offset) { return $this->get($offset); }
public function offsetUnset($offset) { unset($this->data[$offset]); }
}
?>
I've been getting this feeling lately that what we need is a Context object after all. I can't quite put my finger on it, but there've been many questions that've had me thinking of the possibilities of introducing Context. Just a hunch, really, since my comprehension on the whole purpose of the class is a bit lacking, to say the least.Quote:
I also noticed that the filterRequest method of the class InputController modifies the original request.
Bah, I'm probably all wrong anyway. Do correct me - now I have to go get some sleep before I drop on my keyboard.
Funny, I thought you would like the Datasource class as it is really the base class for the Request class and any other class with the main interface of get($name) and set($name, $value) methods.Quote:
Originally Posted by kyberfabrikken
I mainly did it to keep the InputControllerParameter class lightweight and more of a Value Object. I see your point, but when I step back and look at how it is used, there is usually only one (maybe two) $filter and $rule per parameter. I didn't want to create the overhead of a FilterChain for each parameter. What do others think?Quote:
Originally Posted by kyberfabrikken
You caught me! I thought someone might find that and very little slips by you. :)Quote:
Originally Posted by kyberfabrikken
I'll update filterRequest() and make all the rules inherit the Rule class. If I get time I'll try to use kyberfabrikken's form code to extend the Input Controller as a Form Controller. One request for a Form Controller: Does anyone have a clean HTML generation class to use to generate form fields (expecially <select>).
Hmm, if I understand you correctly, by putting a FilterChain/Validator in the InputControllerParameter, it allows you to have multiple filters and rules applied to a single parameter...right? That's the picture I got by reading arborints response.Quote:
Originally Posted by kyberfabrikken
Anyhow, having a FilterChain/Validator for each parameter is probably overkill in most cases, but it can be (really) important sometimes. I think the framework should be capable of having multiple rules/filters applied to a single parameter. We'll introduce a little overhead, but flexibility in this case is more important. It would be a premature optimization not to have this 'flexibility', IMHO :)Quote:
Originally Posted by arborint
I think WACT has one.Quote:
Originally Posted by arborint
Thats what I thought too.Quote:
Originally Posted by Ezku
In that case it begins to make a bit sense. I just couldn't see Request inheriting from DataSource anywhere. I would prefer calling it DataSpace rather than DataSource btw. since source implies that it's read-only.Quote:
Originally Posted by arborint
Quote:
Originally Posted by arborint
I see. Another option would be to put the logic of FilterChain & Validator directly in the InputController class. It's just a forward loop anyway.Quote:
Originally Posted by overunner
That's a valid point. We might want to put the rules in the InputController rather than tie them to a parameter.Quote:
Originally Posted by Ezku
If we do that it seems like the InputControllerParameter is just a valueobject, and as such could be disposed of.
Sorry for going off topic, but...
Wouldn't this reflect more of a Collection, rather than a DataSource, to my way of thinking anyways? The DataSource classes I have have no Iterators at all, I leave it to the DataCollection classes to have an included Iterator, instead :)PHP Code:class DataObject implements Iterator, ArrayAccess
{
Gives you more options no?
I admit, it did get a bit bloated to be a plain data holder. I just added stuff whimsically. :) DataCollection sounds good!Quote:
Originally Posted by Dr Livingston
WACT has a "DataSet"?Quote:
Originally Posted by Dr Livingston
You can currently have multiple filters and validators for each Parameter. kyberfabrikken was proposing that instead of storing them in arrays in each parameter the we have a FilterChain object in each Parameter to hold the chains. I didn't see the need for all the FilterChain objects, but it is the same functionality either way.Quote:
Originally Posted by Overunner
I was waiting until we got through the Input Controller (or further) before going back to the Front Controller code and making any changes.Quote:
Originally Posted by kyberfabrikken
DataSource, DataSpace, DataSet: everybody vote for their favorite. Naming is important because good names improve understanding of the code, but it does get a little maddening. I think we should have a naming round once the code goes through a refactoring phase where people can argue for naming changes.
Two dimensional array seemed a little messy and the InputControllerParameter seemed the easiest spot. Also, I like the FilterChain and Validator a separate classes because they are very useful classes for pages that don't require a full controller. I think part of the goal here is to provide "skeleton scripts" that implement best practices that programmers can both learn from and use as a base for their own frameworks.Quote:
Originally Posted by kyberfabrikken
That is correct. Looking at the series of classes I posted in the skeleton thread the other week there, this is the case (as it should be).Quote:
I would prefer calling it DataSpace rather than DataSource btw. since source implies that it's read-only.
From what I recall, yes? But I've not been back to the WACT site for a long time now :)Quote:
WACT has a "DataSet"?