Tuesday, July 2, 2013

Modularization by extending CAction and using Widgets

Today I will explain how we can modularize certain repeated functionalities using CAction and Widgets. For this case I use one functionality in our project named as ReportAbuse.
Well those are not the only things you will learn in this post. There are multiple other concepts like BaseModel, ExternalActions, Enums etc.,

Business Problem : In earlier stages we used to write report abuse related html and also its companion JavaScript for its behavior. At that time the HTML was of 20-30 odd lines and JavaScript was also about the same so we never had much problem working with it. This process was fine for couple of months but after that we are involved in a situation where we got extra business object (module) for every month so our beloved report abuse code blocks are pretty heavy to maintain in each and every location. Over the time developers are trying to add bells and whistles to that code block to their liking if they have to implement in different place (business object or module) which created fragmentation within the simple code block of report abuse. Spin forward this situation for 3 months and we have 5-10 different templates just for report abuse. This is a big time problem for new developers and existing ones too.

Solution : Since we have started with a single code block for report abuse and eventually ended up with more templates I wanted to standardize the code by using only one template for all. So I wanted to provide this behavior by using widgets combining with other features provided by Yii which I mentioned above.

I have posted some code for you people which consists of modified version of my working code for your ease. In modified version I might change things to generalize to Yii or end up writing some TODOs(Implement these TODOs to your liking and your project requirements).

I have posted the code on BitBucket and one can download it from here
Update : Sorry people above link doesn't give you exact information. Please look into this

Brief explanation about each file, why it is used and what else you can improve on it.
  • ReportAbuseForm.php
This is base form for all report abuse forms, I mean I will use only this form for every request of report abuse throughout application.
Here $context is important and very helpful when dealing with custom business requirements. We can send any information in that $context variable to do some other process and that is the reason it has been an array because we might need to send some heavy customized parameters in future. One can also define different labels for the same form in different locations and the $context variable comes in handy. In questions you may need ‘Comment’ and in groups you may need ‘description’ we can do this by using objectType or context.
  • ReportAbuse.php (Widget)
This is the main code which handles all display of HTML and its behavior using JavaScript. One need to observe that in code I have used $context for profile to display the widget in small size. You can do more customization if you require.
Not all times we need parentId or context so we bind them only if things are set and they are passed separately to customize UI blocks and report abuse thing is sent using $reportAbuseForm for which all necessary data is binded.
  • reportabuse.php (Widget view)
It uses $params to customize UI blocks to render. FormId is made unique so that it doesn’t conflict in DOM. The first part of the script handles all the behavior of sending data through Ajax, validating it, binding errors etc., The second part handles all the behavior of animations when buttons are clicked, clearing forms etc.,. We have used Yii Messages to do string externalization but you can write it there itself but I strongly recommend using string externalization mechanism provided by Yii.
Based on widgetSize different CSS classes have been written to get different effect at different places. Most of the data is set already using hidden Fields and we used this view file only to get user comment about why he is reporting this object.
  • Report.php (External Action by extending CAction)
This is the action called every time we report any object. So changes in this action will reflect throughout the application. To use this action we need a controller so we got it by using $this->getController(); We then validate the form for our rules in ReportAbuseForm.php for null, length validations (If required put some other rules too). Then we used $siteModel to access database and insert the report abuse. We have used this model to consolidate all common logic's like this to one place. We then generalized this method to serve report abuse for all objects (like questions, answers, groups etc.,). Since we are validating only on comment attribute we get errors only for that so we set that error in the error case.
Note : I haven’t tried this scenario with multiple attributes which take user input.
  • SiteModel.php
This is the model which handles all common logics like report abuse, tag search, comment insert (Will discuss this in later post).
reportAbuse method takes $reportAbuseForm and $context as parameters and generates input parameters required for stored procedure (we use stored procedures and java as data layer and yours may be different) then we call the java data methods with necessary parameters to get the job done. In your cases you might need to generate parameters for each stored procedure and call them in switch case or else create a single SQL statement which can handle all those cases or there might be a stored procedure which handles all those logic by giving necessary inputs to it. It all depends on your data access methodologies.
  • SiteController.php
The thing which glues what all we have discussed above is defining this external action to use in our application and we can do this by defining our external action in actions array. We need to initialize our external action like this in actions array
'report'=>'application.components.actions.Report',

By defining like this we get report URL like this <domain name>/report. It might help even in SEO stuff.
Example: http://192.168.0.31/report
More about TagsProvider and CommentsProvider in later posts.

One can use this component by initializing it from view file like this (we needed tipsy but you might not need it)

<div class="reportAbuseWrapper">
    <?php
    $this->widget('ReportAbuse', array(
            'params'=>array(
            'objectId'=>$qaViewModel->question->id,
            'objectType'=>'question',
            'tipsyClass'=>'tipsyleft',
            'authorId'=>$qaViewModel->question->author->id,
           'reportStatus'=>$qaViewModel->question->reportAbuse
        )
    ));
    ?>
</div>


Here is the widget in action

 
This is how the widget looks with all its CSS and JavaScript intact.


This is widget after clicking Report Abuse link in blue color


This is the widget after form validations. Here it is null validation. I mean clicking submit without entering any description.


This is widget after form validations and this time it is maximum allowable characters validation done.


This is confirmation that it is reported and the application will take care of the rest.


This is the URL which it will hit. Of course mine is different. This is the data we are sending for report abuse action.


This is the response after the controller action is called .



One can look into all files which I have mentioned above and change to your requirements.
Please do post your comments on this. If you have any doubt please contact me for more details.

No comments:

Post a Comment