Tuesday, July 9, 2013

Yii: Managing assets

An ability to manage assets is one of the greatest parts of Yii. It is especially useful in the following cases:

  • When you want to implement an extension that stores its JavaScript, CSS, and images in its own folder and is not accessible from a browser
  • When you need to pre-process your assets: combine JavaScript, compress it, and so on
  • When you use assets multiple times per page and want to avoid duplicates

While the first two cases could be considered as bonus ones, the third one solves many widget reusing problems.
Let's create a simple Facebook event widget which will publish and use its own CSS, JavaScript, and an image.

Getting Ready

  • Create a fresh Yii application using yiic webapp as described in the official guide
  • Check that assets directory under application's webroot (where index.php is) has write permissions; assets will be written there
  • Generate and download a preloader image from http://ajaxload.info/

How to do it...

Let's do some planning first. In Yii, you can place your widgets virtually inside any directory and often, it is protected/components. It is acceptable to have one or two classes inside, but when the number of classes increases, it can create problems. Therefore, let's place our widget into protected/extensions/facebook_events. Create an assets directory inside the widget and put inside the ajax-loader.gif you have just downloaded. Also, create facebook_events.css and facebook_events.js in the same directory.
  1. Therefore, let's start with the widget class itself protected/extensions facebook_events/EFacebookEvents.php:
  2. url, urlencode($this->keyword));
        }
    
        public function init() {
            $assetsDir = dirname(__FILE__) . '/assets';
            $cs = Yii::app()->getClientScript();
            $cs->registerCoreScript("jquery");
    // Publishing and registering JavaScript file
            $cs->registerScriptFile(
                    Yii::app()->assetManager->publish(
                            $assetsDir . '/facebook_events.js'
                    ), CClientScript::POS_END
            );
    // Publishing and registering CSS file
            $cs->registerCssFile(
                    Yii::app()->assetManager->publish(
                            $assetsDir . '/facebook_events.css'
                    )
            );
    // Publishing image. publish returns the actual URL
    // asset can be accessed with
            $this->loadingImageUrl = Yii::app()->assetManager->publish(
                    $assetsDir . '/ajax-loader.gif'
            );
        }
    
        public function run() {
            $this->render("body", array(
                'url' => $this->getUrl(),
                'loadingImageUrl' => $this->loadingImageUrl,
                'keyword' => $this->keyword,
            ));
        }
    
    }
    
    
  3. Now let's define body view we are using inside run method protected/extensions/facebook_events/views/body.php:
  4. 
    
  5. We will need to put the following into facebook_events.js:
  6. jQuery(function($) {
        $(".facebook-events").each(function() {
            var url = $(this).data("url");
            var container = $(".data", this);
            $.getJSON(url, function(json) {
                var html = "
      "; $.each(json.data, function() { html += "
    • " + "" + this.name + " "+this.location+"
    • "; }); html += "
    "; container.html(html); }); }); });
  7. Write the following in facebook_events.css created previously:
  8. .facebook-events {
        padding: 10px;
        width: 400px;
        float: left;
    }
    .facebook-events ul {
        padding: 0;
    }
    .facebook-events li {
        list-style: none;
        border: 1px solid #ccc;
        padding: 10px;
        margin: 2px;
    }
    
    
  9. That is it. Our widget is ready. Let's use it. Open your protected/views/site/index.php and add the following code to it:
  10. widget("ext.facebook_events.EFacebookEvents", array(
        'keyword' => 'php',
    ))
    ?>
    widget("ext.facebook_events.EFacebookEvents", array(
        'keyword' => 'jquery',
    ))?>
    
    
  11. Now, it is time to check our application homepage. There should be two blocks with Facebook events named php events and jquery events, as shown in the following screenshot:

How it works...

When we use $this->widget in the site/index view, two EFacebookEvents methods are run: init which publishes assets and connects them to the page, and run which renders widget HTML. First, we use CAssetManager::publish to copy our file into the assets directory visible from the web. It returns URL that can be used to access the resource. In the case of JavaScript and CSS, we use CClientScript methods that add necessary

There's more...

There is more about working with assets.

What is inside the assets directory?
Let's check our assets directory. It should look similar to the following:
assets
1a6630a0\
main.css
2bb97318\
pager.css
4ab2ffe\
jquery.js
Directories such as 1a6630a0 are used to prevent collisions of files with similar names from
different directories. The name of the directory is a hash of complete path to the published
asset directory. Therefore, assets from the same directory are copied to the same place. This
means that if you publish both the image and the CSS file, you can reference images from
CSS using relative paths.

Publishing an entire directory
Using CAssetManager::publish, you can publish an entire directory recursively. The
difference is that single files are monitored after being published, whereas directories are not.

Further reading
For further information, refer to the following URL:
  • http://www.yiiframework.com/doc/api/CAssetManager
  • http://www.yiiframework.com/doc/api/CClientScript
  • http://www.yiiframework.com/doc/api/CHtml#asset

0 comments:

Post a Comment