📜 ⬆️ ⬇️

ExtJS Application Architecture: Zend Framework Approach

In addition to oddy articles about ExtJS-based RIA application architecture, I want to offer my own, alternative approach to this problem. It consists of using a ZendFramework-like xFrame framework written in JavaScript. Under the cut - a description of the key elements of the system, links to the demo application and the source code.

The xFrame story began with a single order. The client wanted to implement on its website a system for viewing fin. reporting. He was already familiar with ExtJS (the site admin panel was implemented on it), he liked the library and he wanted to display reports in the form of ExtJS Grid'ov. Additional system requirements were the differentiation of access rights to view reports to different groups of users. Thus, I was faced with the task of implementing an RIA application with authentication and distribution of access to resources.

The admin panel of the site supported such features, but it was made in the form of a WebDesktop, which was not suitable for the client. Therefore, I began to develop the system from scratch. At that time I was just reading the book by K. Zervas "Web 2.0: Creating Applications for PHP" and imbued with the spirit of the Zend Framework described in it. And I wanted to create exactly something similar to ZF. And I did it. The basic features were implemented within two days, a couple of days took debugging. Later, after the implementation of the reporting system itself, I, in my free time, brought my framework to the state in which it can be presented and decided to present it to the public.

I have not yet invented the final name for the framework, while calling it the “code” name xFrame (eXtjs FRAMEwork). xFrame implements:
')


Models in xFrame are implemented based on Ext.data.Record. The class (constructor) of the model is created using the Ext.data.Record.create ([]) method. If you need to encapsulate additional methods in the model, use Ext.override () :
Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  1. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  2. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  3. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  4. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  5. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  6. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  7. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  8. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  9. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  10. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  11. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  12. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  13. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  14. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  15. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
  16. Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .
Application.models.News = Ext.data.Record.create([ {name: 'Id' , type: 'int' }, {name: 'Permalink' , type: 'string' }, {name: 'Title' , type: 'string' }, {name: 'Brief' , type: 'string' }, {name: 'Text' , type: 'string' }, {name: 'DateCreated' , type: 'date' , dateFormat: 'Ym-d' }, ]); Ext. override (Application.models.News, { getLink : function () { return App.route({ permalink : this .get( "Permalink" ) }, "news" ); }, getDateCreated : function () { return this .get( "DateCreated" ).format( 'm/d/Y' ); } }); * This source code was highlighted with Source Code Highlighter .

Also, for each model, the application automatically creates two “static” (called from the constructor, not from an instance) the createStore (add_cfg) method (creates a config object for the data store with model fields) and createOne (hash) (creates a model instance and loads into fields from a hash object).

The controllers are implemented by analogy with ZF. To create a controller with, you need to create a class <Controller name> Controller , to create an action, use the <Action_name> Action method. Three parameters are passed to the action handler: a hash with the parameters passed to the action, a link to the application object, a link to the ViewPort object.
  1. Application.controllers.FrontController = Ext.extend (Application.controllers.Abstract, {
  2. .................................................. .................................................. ...................
  3. newsAction: function ( params , app, panel) {
  4. panel.add ({
  5. xtype: 'Application.components.NewsViewer' ,
  6. newsPermalink: params .permalink
  7. });
  8. },
* This source code was highlighted with Source Code Highlighter .


The presentation functionality is implemented entirely on ExtJS components. When an application is initialized, a viewport is created which is rendered at a specified location on the page. The output area changes when you change controllers / actions. All other content remains unchanged. Before calling the action method, the component output area is cleared, the action itself can add the necessary components to it, after which the application will redraw the viewport.

At the structural level in the system, the following key components are distinguished:

The Application class is the basis of the application. In this class all other system components are encapsulated.


Commonly used methods:



When you create an application, you must inherit this class and predefine the necessary initialization methods:

  1. var App = new (Ext.extend (Application, {
  2. renderTo: 'app' ,
  3. autoRun: true
  4. initAcl: function () {
  5. this .constructor.superclass.initAcl.call ( this );
  6. this .acl.addRole ( new Application.Acl.Role (....................));
  7. .................................................. ....................
  8. },
  9. initRouter: function () {
  10. this .constructor.superclass.initRouter.call ( this );
  11. this .router.addRoute ( new Application.Router.Route (....................................} ))
  12. }
  13. }));
* This source code was highlighted with Source Code Highlighter .


The Application.Acl class is an authorization system. The principle of operation is similar to Zend_Acl: allows you to define a set of resources, roles, and permissions to access each role to each resource.

Commonly used methods:


Example of use:
  1. initAcl: function () {
  2. this .constructor.superclass.initAcl.call ( this );
  3. // add guest role
  4. this .acl.addRole ( new Application.Acl.Role ( "guest" ));
  5. // add user role which inherits guest role permissions
  6. this .acl.addRole ( new Application.Acl.Role ( "user" , "guest" ));
  7. // by default, close all access to all
  8. this .acl.deny ( null );
  9. this .acl.deny ( "guest" );
  10. this .acl.deny ( "user" );
  11. // permissions for the guest role
  12. this .acl.allow ( "guest" , "front / index" );
  13. this .acl.allow ( "guest" , "front / test" );
  14. this .acl.allow ( "guest" , "front / news" );
  15. this .acl.allow ( "guest" , "front / action1" );
  16. this .acl.allow ( "guest" , "front / action2" );
  17. this .acl.allow ( "guest" , "user / login" );
  18. this .acl.allow ( "guest" , "user / noaccess" );
  19. // user roles give access to user / logout / restricted / index and prohibit logins
  20. this .acl.allow ( "user" , "user / logout" );
  21. this .acl.allow ( "user" , "restricted / index" );
  22. this .acl.deny ( "user" , "user / login" );
  23. this .acl.setErrorRedirect ( null , null , {controller: 'user' , action: 'noaccess' });
  24. }
* This source code was highlighted with Source Code Highlighter .


Application.Router - managed routing. Allows you to create "beautiful" links instead of / controller / action / params by default.

Methods:


Examples:

  1. .................................................. .......................................
  2. this .router.addRoute ( new Application.Router.Route ( "news" , "news /: permalink" , { "controller" : 'front' , "action" : 'news' , "permalink" : "[\\ w \\ d \\ -] + " }));
  3. .................................................. .......................................
  4. getLink: function () {
  5. return App.route ({id: this .get ( "Id" ), permalink: this .get ( "Permalink" )}, "news" );
  6. }
  7. .................................................. .......................................
* This source code was highlighted with Source Code Highlighter .


Application.Identity - user data. The class is not implemented in full. In the demo application, it stores session data in cookies using Ext.state.Manager. When logging in, the actual authentication does not occur, the object sets the property isLogged to true and saves the user name and the user role. In the future, it is planned to make a complete analog of Zend_Auth with various adapters for authentication verification.

At this link you can see a demo application. It implements viewing the blog feed, login to the system, access to the “closed” section. The source code is available for download here ( alternative link , without ExtJS distribution). Works with ExtJS 3.1, all modern browsers are supported (IE8, Opera10, FF3 +, Safari 4, Chrome).

Any suggestions / wishes / ideas / remarks / corrections are accepted. In general, I have a desire to make a full-fledged open-source framework for RIA applications out of the framework. If among you, dear Habr's readers, there are interested, as well as have the desire and free time - welcome to the project team.
UPD. Transferred to the "ExtJS Library"

Source: https://habr.com/ru/post/88208/


All Articles