Component oriented framework

Time ago I tried to represent my framework: https://github.com/igor1999/gi

Old thema: PHP framework (HMVC, global context, ORM...)

Now I made there quite important changes and written Readme with help of professional translator.

So please welcome… Any criticue acceptable.

I remember, that in old thema about my framework I was asked: Why do I create HTML-Markup with so complex way.

Answer is: because HTML-Markup itself could be quite complex. And if you just print it out, you should to repeat a lot of same markup parts. I think, it’s clear, this is no good.

I use, for Markup rendering two concepts. Below I will quote Readme from my framework: https://github.com/igor1999/gi

In addition to horizontal layers, we see a number of vertical rectangles 
in the diagram. These rectangles are components.

A component is a collection of PHP classes and other resources located 
in the same directory that is responsible for the creation and operation 
of a separate frame of a web document: menu, table, list, form, etc…

Also, one component can be an aggregator for other components. 
The most commonly used of these aggregators is the Layout component, 
which contains the components responsible for navigation, 
authorization and other things...

The component is responsible for the full cycle of functioning of its frame - 
from client events processed by java-scripts to writing to persistent storage.

However, as can be seen from the diagram, classes and resources belonging 
specifically to this component are in the lower two and a half layers. 
Classes from the top two and a half layers are outside the components 
and are called and used if required.

........

The GI\Component framework package contains a number of standard components 
that can be used either directly or as a basis for creating your own components 
needed in the current project.
 DOM objects.

The GI\DOM package contains classes for rendering XML and HTML elements. 
Once we have instantiated such a class, we can configure it by setting the tag, 
attributes, classes, style attributes and so on, and then using 
the toString() * method to get the appropriate XML or HTML markup.

The above package contains classes that correspond to the main XML 
and HTML elements, attributes, text nodes and a host of others…

For example, this package has the Layout class, which allows you to automate 
the creation of a layout based on divs with the float property.

An important advantage when using such classes is the ability 
to reuse markup generator classes through inheritance, composition, etc... 
Therefore, when dealing with clearly structured views, such as tables, forms, 
lists, etc, it is better to use DOM classes.

GI deliberately abandons the __toString() magic method. The reason is that 
an exception in this method results in an error.

I sort of clicked through your repo. I could not understand most of the README file. But I noticed you had a Login component. Perhaps we could compare details of our respective approaches?

Here is my login action which is invoke by /login and renders a login form. Actual working Symfony based code:

class UserLoginAction implements ActionInterface
{
    public function __invoke(PageTemplateInterface $pageTemplate, AuthenticationUtils $authenticationUtils) : Response
    {
        $error = $authenticationUtils->getLastAuthenticationError();
        $lastUsername = $authenticationUtils->getLastUsername();

        return new Response($this->render($pageTemplate,$lastUsername,$error));
    }
    private function render(PageTemplateInterface $pageTemplate, string $lastUsername, ?AuthenticationException $error) : string
    {
        $stylesheet = <<<EOT
  <link rel="stylesheet" type="text/css" href="../assets/login.css">
EOT;
        $pageTemplate->addStyleSheet($stylesheet);

        $lastUsername = $this->escape($lastUsername);

        $content = <<<EOT
<form method="POST" class="form-signin text-center">
  {$this->renderUser()}
  {$this->renderError($error)}

  <img class="mb-4" src="../assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
  <h1 class="h3 mb-3 font-weight-normal">Please Log In</h1>

  <label for="inputEmail" class="sr-only">Email address</label>
  <input type="text" id="inputEmail" name="slug" class="form-control" placeholder="User name or email" required autofocus value="{$lastUsername}">

  <label for="inputPassword" class="sr-only">Password</label>
  <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>

  <div class="checkbox mb-3">
    <label><input type="checkbox" name="remember_me" value="remember-me"> Remember me</label>
  </div>
  
  <input type="hidden" name="_csrf_token" value="{$this->getCsrfToken('authenticate')}">
  <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
  <p class="mt-5 mb-3 text-muted">&copy; 2017-2020</p>
</form>
EOT;
        return $pageTemplate->render($content);
    }

So __invoke pulls out any previous user info ($lastUsername and authentication errors) and passes it to the render method.

PageTemplate is a view class which takes care of rendering the html head section and a few other boring details. I can provide more details if needed but using an OOP approach here avoids some of the issues associated with more conventional flat file template systems such as Twig.

Render first adds a css link to the page template then uses heredoc to render the login form. Having used html since the mid-nineties I personally think it easy to understand the markup. Especially when html has so many potential attributes.

Packaging the __invoke and render methods in one class avoid the need to somehow relate the view to the action. And it makes passing data from the action to the view easy.

I’d be very curious to see your equivalent login form. Especially the details.

Authentication component (login or logout, dependently of session data):

Dialog component. Abstract, base for components with moved/resisable dialog window:

Identity. Object for authentication processing. Uses session exchange mechanism:

And others in framework…

Views.

Unlogged user: sign in.bmp (1.9 MB)

Login dialog opened: sign in with dialog.bmp (1.9 MB)

Login dialog moved: sign in with dialog moved.bmp (1.8 MB)

Logged user: sign out.bmp (1.7 MB)

Not quite what I was hoping for. I’m focused mostly on the actual rendering based on our conversation in the PDO thread. Did some more digging and came across this:

namespace GI\Component\Authentication\Login\Dialog\View;
class Widget extends AbstractWidget implements WidgetInterface {
...
    protected function createLoginTextbox()
    {
        $this->loginTextbox = $this->getGIDOMFactory()->getInputFactory()->createText(
            $this->getViewModel()->getLoginName()
        );

        $this->loginTextbox->getAttributes()->setPlaceholder(
            $this->giTranslate(GlossaryInterface::class, Glossary::class, 'login')
        );

        return $this->loginTextbox;
    }

I’m assuming this is where your login input element is being defined. The rendering takes place elsewhere. I contrast this with:

  <label for="inputEmail" class="sr-only">Email address</label>
  <input type="text" id="inputEmail" name="slug" class="form-control" placeholder="User name or email" required autofocus value="{$lastUsername}">

I look at your code and, not being familiar with your rendering technique, wonder if there is a label or if any css classes are being applied, is there an autofocus option. Etc and etc. Even if I was more familiar with your system I think there might still be a big disconnect between creating the widget and the output. Especially if you are trying to debug html/css issues after some time has passed.

With my approach I pretty much know exactly what is being emitted.

I have tried your approach several times. The problem is that html/css is rather complex to abstract. All kinds of little attributes that you may or may not want for a given situation. I personally was never able to come up with an approach that made more sense then just writing the html.

I do wish you the best of luck with your framework. Hopefully it will attract more attention as time goes by.

1 Like

CSS covered by this class:

and rendered here: https://github.com/igor1999/gi/blob/master/GI/Component/Dialog/View/AbstractWidget.php

    public function toString()
    {
        return $this->create()->getResourceRenderer()->toString() . $this->container->toString();
    }

DOM-objects used for views with “strong” structure, as form or table. Attribute of DOM-object could be easy changed. For views with “weak” structure phtml-templates recommended.

Your concept has a very typical problem. You say:

Why should I do it so complex? I just write HTML quickly and directly in PHP-class.

But what would you do if your markup needs a changes? For me it’s not a problem, I will extend class Widget and e.g. add a new element as DOM-object. And you with your solid HTML-string?..

All “easy written” projects growing up to something terrible, with it absolutly impossible to work.

If my markup needs changing then I would change it. Clearly we are not communicating.

I often run into this sort of argument. I then ask for a link to the source for at least one application that is “grown up”, in production, has stood the test of time and is being actively maintained. Perhaps you will be the first to provide such a link?

I mean, if you need somewhere else similar, but not equal form.

What? :grinning: You doubt of terrible PHP-projects existence? Really?

I working as freelancer since 2007. 90% of my employers need freelancers, as me, exactly, because they have huge and unreadable project, that makes helpless their IT-team.

Сoncrete firm names I save by me, sorry. Lawyers are expensive in our world.

Give me a specific example please. It’s a login form. Eventually I’ll add some forgot username/password links but it’s a simple form. What sort of similar form would I want? Ideally you would of course show me exactly what changes you would need to make to accomplish the same thing. Then we could compare details.

Like I said, we are obviously not communicating. I asked for an example of a good application based on your code. Did not say anything about terrible projects though I would be interested in seeing one based on my approach. As far as I know, I’m the only one that really does things this way in PHP and talks about it. Facebook has React which is basically the same approach as mine. I guess you could call the Facebook app terrible though it does seem to have quite a few users.

In login form could be checkbox “save login”. And actually, either this checkbox required, could be dependend from usecase (admin part or common part).

But this question is incorrect at all. I talk about general principle - form could be extended. Creating form and edition form; form for one or for another kind of user.

E-e… Actually, my framework not shared. And single example, that I have is my test projects. Complete code and references - in my Githab repository.

React is JS client-side library. Guys from JS - they are very funny sometimes. And actually AngularJS based itself on HTML-templates.

I am sure that Facebook usability is too bad. And I heard and I could believe that code of its application the same.

We were talking about the login form (or at least I was). I think you are now talking about a registration form and possibly editing the profile of an existing user? Maybe with some admin oriented mods? Great. This is all basic OOP stuff. We can certainly make a registration form. I personally would not start with a login form but maybe you do.

If an admin form has some additional fields then you check for admin and add them. I don’t really understand where you think the problem is. If two forms are similar then maybe I would a make a base form and then extend it. Or maybe use composition.

None of this is a problem and if you are willing to give me an actual use case that you have implemented then I’ll glad to implement it myself. But I need “what is” type info and not “what if” hand waving.

I hate to say this because I am enjoying the conversation but so far your only argument seems to be “Your code is bad because I say so”.

1 Like

I don’t understand, what you don’t understand.

Let’s login form. In this form there is checkbox “save login”. If this checkbox checked by last login, it saved in cookie with endless expiration, and user needs no more login.

Than comes a new task. We will have not only common user, but also superviser. And for superviser this ability “save login” is unsuitable. We should to remove the checkbox from form.

For me is not a problem. I will create extended component from my login and in Widget of this new component I remove DOM-object Checkbox from DOM-object Layout.

Your form is solid piese of HTML. My question: how would you remove checkbox in case above?

Okay. I don’t know how you can tell in advance that user is an admin without them first logging in but that is okay. Let’s assume we can set an $isAdmin variable.

You may have noticed that I already have a remember_me checkbox. We need to make this conditional so we add a renderRememberMe() method:

private function renderRememberMe(bool $isAdmin) : string {
    if ($isAdmin === true) return '';
    return <<<EOT
  <div class="checkbox mb-3">
    <label><input type="checkbox" name="remember_me" value="remember-me"> Remember me</label>
  </div>
EOT;
}
// And then adjust the original template
private function render(PageTemplateInterface $pageTemplate, string $lastUsername, ?AuthenticationException $error, bool $isAdmin) : string {
...
<label for="inputPassword" class="sr-only">Password</label>
  <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>

  {$this->renderRememberMe($isAdmin)}
  
  <input type="hidden" name="_csrf_token" value="{$this->getCsrfToken('authenticate')}">
...

And that should do the trick.

You could also do this without adding another method but that quickly gets messy.

E.g. user signs in usecase for administrators only in. This could be extra authorisation.

And once more, I speak for any form not exactly for login. That’s why argument: “In login this is impossible” not correct. There are a lot of forms, they need thouse changes.

Uhu… And for some second change you add some another method, for third - another one…

Can you heared about Open-Closed-Priciple? Which means: existent code should be opened for extensions, but closed for changes. Otherwise, any change needs complete testing. Congradulation, you threw that priciple in the garbage.

I suspect you are just being a bit silly now.

Lets take look at your widget code I mentioned in my first post:

    protected function createLoginTextbox()
    {
        $this->loginTextbox = $this->getGIDOMFactory()->getInputFactory()->createText(
            $this->getViewModel()->getLoginName()
        );

        $this->loginTextbox->getAttributes()->setPlaceholder(
            $this->giTranslate(GlossaryInterface::class, Glossary::class, 'login')
        );

        return $this->loginTextbox;
    }

Instead of a placeholder with a value of ‘login’ the customer wants it to say ‘logon’. Could happen but alas the class is closed for modification. So I need to extend it, copy paste the complete createLoginTextbox method and adjust.

But oh oh. We also have a private property called $resourceRenderer. Looks like we need to clone the constructor as well. Anything else? We really should checkAbstractWidget just to make sure.

Now we need to change the wiring. How to tell the system to use LogonWidget instead of Widget? Looks like:

namespace GI\Component\Authentication\Login\View;
class View extends Base implements ViewInterface
{
    public function __construct()
    {
        parent::__construct();

        $this->widget = $this->getGIDi(WidgetInterface::class, Widget::class);
    }

Looks like we extend another class and clone another method. Deep sigh.

Note: I have not explored in detail how your service locator works. I suppose it is possible that it can be convinced to return LoginWidget when asked for Widget. But even if it could I suspect you would have to clone whatever class is wiring up the service locator to make the change.

And gosh darn it, the View class has some private properties as well. More cloning. And then we need to figure out how to wire in LogonView instead of View.

Maybe an overly strict and out of context interpretation of the Open-Closed Principle really does belong in the garbage?

So come on. If I know I need an admin version of a form then I’ll plan accordingly. And I’m not going to somehow freeze every class in my application and only make changes through new classes. You mentioned testing. Testing is a good thing and when I make changes I test them before releasing. Regardless of how the software is designed.

Actually I should to save ‘login’ in constant. But replace ‘login’ with ‘logon’ looks like replace ‘open’ with ‘now open’. So that could be my error, but that’s nothing with principle.

For what? Do we have a new style or JS? And actually, all private properties accessable with public or protected getters.

With DI-container probably. DI-container set dependently of module or even concrete request. And if for this request we need LogonWidget - no problem.

If I need two different forms in different requests, then I need to extend only Widget class.

Yes, if you have some complex class hierarchy and need to extend some node in this hierarchy that causes changes on whole hierarchy complete. But nothing to do with that. This is normally. And this is any way better than to have universal class and change it permanently. Actually, thouse class - typical anti-pattern. About problems with solid HTML-string, that makes itself bigger and bigger, and more and more unreadable, I already said.

Check your widget code:

    public function __construct()
    {
        $this->resourceRenderer = $this->getGIDi(
            ResourceRendererInterface::class, ResourceRenderer::class
        );

No setter there. Your class is effectively final without copy/pasting code and/or properties.

So now we are back to “code is bad because I say so”. Oh well. Have a nice weekend.

I don’t understand… New resourceRenderer required, if there is the changes in CSS or JS.

In this case, first option - create new ResourceRenderer, that extended old, and bind it with DI-container.

Second option - create new Widget private property resourceRenderer, set it in constructor and override getter. All Widget methods use not private property resourceRenderer directly, but protected getter. So, what’s the problem?

But actually I understand the problem. I mean, I understand, why you hold with all your energy on this quite simple login form. Look, I have added checkbox and everything’s fine.

Let’s change the task… There are four forms: Offer, Contract, Invioce and Delivery. This forms have around 40 common fields. And any of forms has around 10 its own unique fileds (with labels, placeholders, JS-events…).

Should I explain, what happens, if you try to solve this task with your concept? I mean, how will your code look like, and what saying your teammates, by work on it? Or you able to guess without my tip?

Great. Details please. Exactly which fields does each form have? And what exactly does each field need to do? Even screen copies will suffice as a starting point but I would rather see your code.

As I unerstood, you don’t believe in existence of so complex forms? Actually, this example is from my former project. Sales, insurance, HR, industry, science… Forms with 50 fields there not only exist… They are not realy big. How about 200 fields (also from my former project)?

Your concept: print out whole HTML flat in single method with Heredoc operator. Your argumentation: that is very simple and readable.

Even in small and simple form “Login” you steped from your concept aside. You packed part of your markup in external method. How about “readable” in this case?

But if you would have a real big form, as in my example…

…Either you break you concept completely up and pack almost all your markup in external methods. Then you probably will come to idea to pack this external methods in external classes… And slowly you will step in direction of my concept.

…Or you hold your concept despite of all problems. Then you will repeat and repeat and repeat same HTML many times.

That’s all I could to say by this thema.