Kontaktieren Sie uns: +49 (711) 46 97 28 - 80|info@teqneers.de

Sencha Ext JS meets Symfony

Sencha Ext JS is a MVC/MVVM JavaScript framework for building cross-platform web applications targeting desktops and tablets, and smartphones. With its version 6 (released in July 2015) Sencha merged their mobile-specific Sencha Touch framework and desktop counterpart Ext JS into a single framework keeping view-related code separate on top of a unified core that provides common framework functionality. Together with Ext JS 6 comes a complete application toolchain called Sencha Cmd – a Java- and Javascript based command line tool that serves as CSS compiler (using SASS), Javascript compiler, package manager and compressor and resource manager. Even though the whole build process is highly configurable (down to custom Ant-based tasks), integrating an application built on top of Sencha Cmd into a server-side web application is cumbersome and requires a lot of configuration and boiler-plate code on the server. This is especially true because applications based on Sencha Cmd differentiate between production and development mode. While production mode requires a fully built application package, development mode can be run just from the uncompiled and unpacked source files – a must during development. These two modes have different characteristics with different requirements, esp. when not running the Sencha Cmd built-in development server but running on a full server-side application stack (e.g. backed by Apache and PHP). To mitigate this difference when developing Ext JS 6 based application on basis of a Symfony PHP web application, we created a Symfony bundle (teqneers/ext-application-bundle) and an accompanying library (teqneers/ext-application) hat we’re sharing openly on Github now.

The teqneers/ext-application library is currently just used as the foundation of our Symfony bundle teqneers/ext-application-bundle. We have not tried to use the library as a stand-alone component or in any other context than a Symfony environment. We’d appreciate any help and contribution to make the library more useful on its own.

Following up is a quick walkt-through for settings up the Ext JS 6 demo application (that is created when starting developing an application using Sencha Cmd) running on top of a Symfony PHP application.

We’re following the official installation documentation for Symfony to setup a working application skeleton. Assuming you have installed the Symfony installer according to the documentation, we’re starting with:

symfony new my_example
cd my_example
php app/console server:run // CTRL+C to stop

You now have a running Symfony application on localhost port 8000 using the built-in PHP web server. Test if it loads correctly in your browser. Next we’re going to create a basic Ext JS 6 application. Again assuming you have installed Sencha Cmd according to the documentation, we can do:

sencha generate app -ext -classic MyApp my-app

This downloads the latest Ext JS free trial version and builds an application skeleton for a classic application called MyApp in folder my-app.  Check „Using Sencha Cmd with Ext JS 6“ for more information on what this command does in detail.

To test that everything worked as expected, change into the application folder, and run the Sencha Cmd built-in web server.

cd my-app
sencha app watch // CTRL+C to stop

You now have a Ext JS application running in development mode on localhost port 1841. Test if it loads correctly in your browser. Next stop: installing the teqneers/ext-application-bundle. Symfony applications leverage the services provided by Composer, a PHP package manager. To install either locally inside the project or globally on your system, please follow the installation guide. Now we’re adding the required dependencies.

cd .. // don't forget to go back to our application root
composer.phar require teqneers/ext-application-bundle:dev-master teqneers/ext-application:dev-master

Please note that even though teqneers/ext-application is a dependency of teqneers/ext-application-bundle you’re required to list them both explicitly because both packages are not yet available in a stable version. Following common procedures to add bundles to Symfony applications, we’re going to modify AppKernel::registerBundles() in app/AppKernel.php:

// app/AppKernel.php
// in AppKernel::registerBundles()
$bundles = array(
    // ...
    new TQ\Bundle\ExtJSApplicationBundle\TQExtJSApplicationBundle(),
    // ...
);

Doing this also forces us to update the application configuration by adding the required teqneers/ext-application-bundle settings. This is done in app/config/config.yml.

// app/config/config.yml
// ...
tq_ext_js_application:
    app_path: '%kernel.root_dir%/../my-app'
    builds:
        default:
            development:
                build_path: build/development/MyApp
                microloader: /bootstrap.js
                manifest: /bootstrap.json
                app_cache: ~
            production:
                build_path: build/production/MyApp
                microloader: microloader.js
                manifest: app.json
                app_cache: cache.appcache

Well, this deserves some additional comments though. The app_path variable points to the full path of the Ext JS application we created above. One can leverage Symfony’s container parameters to create a path relative to the kernel root directory. The builds configuration section contains information about the build profiles configured in the Ext JS application. In our example we just use one build profile – the name default is arbitrary, it’s just used to reference a build in multi-build setups. Each build configuration is split into development and production settings respectively. The development section defines the application environment used in development mode (running the Symfony application from console server:run --env=dev or app_dev.php). The production section on the other hand configures the application environment for production usage (running the Symfony application from console server:run --env=prod or app.php). build_path is the path to the directory containing build artifacts produced by either sencha app build (production) or sencha app watch (development). This path is relative to app_path. microloader, manifest and app_cache define the files containing the Ext JS micro loader, the application manifest and the application cache manifest respectively. Paths starting with a / are relative to app_path, while paths without a leading / are relative to build_path. This is important to keep in mind. In the given example configuration that means that manifest and micro loader are served directly from the application directory in development mode while their respective build artifacts are used in production mode.

Because the teqneers/ext-application-bundle comes with its own controller and requires some routes to access the controller actions, we need to update our routing to include the routing information provided by the bundle. Just edit app/config/routing.yml:

// app/config/routing.yml
// ...
ext_app:
    resource: "@TQExtJSApplicationBundle/Resources/config/routing.yml"
    prefix: /

The application should now run again just as before – even though we’ve not integrated any Ext JS based page yet. To do that we start by editing the indexAction on the default controller in src/AppBundle/Controller/DefaultController.php:

// src/AppBundle/Controller/DefaultController.php
// ...
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        return $this->render('default/ext.html.twig');
    }
// ...

This action is as simple as it gets – it just renders the app/Resources/views/default/ext.html.twig view that we’re creating next.

// app/Resources/views/default/ext.html.twig
<!DOCTYPE HTML>
<html manifest="{{ extjsAppCachePath() }}" lang="en">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="UTF-8">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>Welcome!</title>

    <script type="text/javascript">
        var Ext = Ext || {};
        Ext.manifest = '{{ extjsManifestPath()|e('js') }}';
    </script>
    <script id="microloader" data-app="{{ extjsApplicationId() }}" type="text/javascript" src="{{ extjsBootstrapPath() }}"></script>
</head>
<body>
</body>
</html>

The template uses extjsAppCachePath(), extjsManifestPath(), extjsApplicationId() and extjsBootstrapPath() Twig functions to output the correct paths to the application cache manifest, the application manifest and the application micro loader as well as the application ID (used to handle client-side local storage caching). Restarting the development web server and navigating to http://localhost:8000/ will show the Ext JS demo application but served via our Symfony based backend.

There’s one small problem however when trying to run the application in production mode. First of all we have to build the production package for our application first.

cd my-app
sencha app build

But secondly our application is configured to use a so-called embedded micro loader in production mode by default. This means that the micro loader code is literally copied into the index page of our application (by default that’s index.html). To see how that looks like you can take a look into my-app/build/production/MyApp/index.html. There are two ways to solve this issue. First is to copy the micro loader code from the index.html file into our app/Resources/views/default/ext.html.twig template. Second option (that’s the one we’re going to use) is to tweak the application build configuration so that Sencha Cmd produces an external micro loader build artifact. Open my-app/app.json and change lines 425 to 430 to match:

// my-app/app.json
// ...
"output": {
    "base": "${workspace.build.dir}/${build.environment}/${app.name}",
    "microloader": {
        "embed": false
    },
    "appCache": {
        "enable": false
    }
},
// ...

This instructs Sencha Cmd to disable the embedded micro loader. Run

sencha app build

again. This produces my-app/build/production/MyApp/microloader.js which is being picked up by our bundle and served as application micro loader. Now we can serve our application in production mode using:

php app/console server:run --env=prod // CTRL+C to stop

That’s how to integrate an Ext JS application into a Symfony based backend. There are dozens of things to configure to tweak and to change in your Ext JS application build configuration. Keep in mind that some of these settings might require you to reconfigure the bundle as well or are even not supported at the moment (serving an embedded manifest for example).

Please feel free to contribute either by discussing with us, by testing and reporting bugs, by requesting features or even by actively working on the package itself. The package is available on Github: github.com/teqneers/ext-application-bundle, the respective core library can be found on Github as well: github.com/teqneers/ext-application.

Watch out for a follow-up article on integrating Ext.direct, an AJAX-based RPC-communication framework that’s part of Ext JS, into your Symfony application.

Versions used: Symfony 2.7.4, Sencha Cmd 6.0.1.72, Sencha Ext JS 6.0.0.640, teqneers/ext-application be2b95, teqneers/ext-application-bundle 4a0159

By | 2017-05-19T09:30:05+00:00 September 15th, 2015|Development|5 Comments

5 Comments

  1. Cipher 21. Dezember 2015 at 8:47 - Reply

    the document is really helpful for me. but I have one question.
    in your case, you just use extJS classic mode, if I want use classic and modern mode in my project.
    how to setup config file?

    right now, my setting is below:
    tq_ext_js_application:
    app_path: ‚%kernel.root_dir%/../myApp‘
    builds:
    default:
    development:
    build_path: build/development/MyApp
    microloader: /bootstrap.js
    manifest: /classic.json //here is the different
    app_cache: ~
    …….

    is it right?

    Thank you.

  2. Cipher 21. Dezember 2015 at 8:49 - Reply

    Sorry, I didn’t read Git issue first. please remove my post. thank you.

  3. Marius Kuhn 8. Januar 2016 at 12:06 - Reply

    Thanks for the awesome tutorials and bundles you provide!

    I have a problem with the ExtJS resource path however.
    While everything works fine for js and css files, I’m unable to resolve some other resources (which reside inside the ExtJS resources directory) like images for example.

    I’m used to accessing my resources with the Ext.getResourcePath() method when it’s not possible to use a css class and defining the image there.
    This is not possible however, because Ext.getResourcePath() returns the normal build path (e.g.: „build/development/myApp/resources/images/myimage.png“).

    Due to Symfony routing and your bundles, it is required to acces resources differently.
    For development for example:
    „/app_dev.php/resources-default-dev/resources/images/myimage.png“

    Any insight on how it’s possible to „tell“ ExtJS how to access resources would be appreciated.

    • Stefan Gehrig 8. Januar 2016 at 12:22 - Reply

      Hi Marius,

      it seems like there is something missing in the component that rewrites the manifest before delivering it to the client. It rewrites the js and css keys as well as paths and loadOrder in development mode, but it doesn’t rewrite the resources key (seems as if we never used these values). The problem is in \TQ\ExtJS\Application\Manifest\ManifestLoader::loadManifest() and must be fixed before you can use Ext.getResourcePath().

      Can you please open an issue on https://github.com/teqneers/ext-application/issues ?

      Thanks for your efforts.
      Best regards

      Stefan

Leave A Comment