Yii
Yii Framework Related Posts
How to use a Widget as an Action Provider
2
At the Yii forum there a good question about this matter, and most of us where curious on how this feature actually works.
As usual in the API docs it was clearly written but sometimes the text just sounds like a test for a car driver license. Nevertheless, after creating a test scenario, I found the solution and this is article is to show you exactly how this is done.
Why would I need an action provider?
Well, imagine you have lots of general actions that could be shared among controllers. It is true that by setting the actions() function to point to the external CAction classes files but just imagine that those functions are encapsulated by just a class (a widget in this case) and you just need a line of code to import all of its actions.
First Step: Create your Action
For the sake of the article we creating an action named getData that supposed to be shared among the whole project and saved with the name getData.php on our protected/components/actions folder.
<?php
class getData extends CAction{
public function run(){
echo 'HELLO WORLD';
}
}
Second Step: configure the Widget
To transform a Widget into an action provider is quite easy (once you know of course). The only thing we need to do is to set the static method actions(). As you will see on the following code, we name the action as GetData and that is the action that will be called in our route. We are going to save the following widget in our protected/components/ folder with the name testProvider.php.
class testProvider extends CWidget{
public static function actions(){
return array(
// naming the action and pointing to the location
// where the external action class is
'GetData'=>'application.components.actions.getData',
);
}
}
Step 3: Configure our Controller
Finally we set our controller’s actions() function to point to our actions provider.
// This function is in this example
// on SiteController
public function actions()
{
return array(
// test. is the prefix we are going to use
// for all action within the actionProvider class
// we point to the location where the provider
// is
'test.'=>'application.components.testProvider',
);
}
Now we can call the action as controllerID/actionPrefix.actionID
index.php?site/test.GetData
Agile Web Application Development with Yii 1.1 and PHP5
Tweet this!
Yii News: Query Caching
5
Starting from version 1.1.7 Yii Framework will include a really great feature: query caching. This new feature is built on top of data caching, query caching stores the result of a DB query in cache and may thus save the DB query execution time if the same query is requested in future, as the result can be directly served from the cache -After this, I truly would like to see some proper benchmarks versus other frameworks out there on query requests.
Using Query Caching with DAO
We need to use CDbConnection::cache() method in order to perform DB queries. The following example specifies that the query results should remain valid in cache for 1000 seconds. If we execute the same lines of code within the next 100 seconds one more time, the query results will be returned from cache without executing the SQL statement again.
$sql = 'SELECT * FROM tbl_post LIMIT 20'; $rows = Yii::app()->db->cache(1000)->createCommand($sql)->queryAll();
We can also specify a cache dependency for the cached query result so if there is any changes to the table, then we can invalidate the query results. In the next code, we create a dependency which checks the maximum `update_time` of all records in a table. If the value has any change, it means the table data is changed and we should invalidate the query cache.
$sql = 'SELECT * FROM tbl_post LIMIT 20';
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
$rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
Using Query Caching with CActiveRecord
This new feature can also be used with CActiveRecord
$posts = Post::model()->cache(1000)->findAll();
// query caching can also be used with relations
$posts = Post::model()->cache(1000)->with('author')->findAll();
If you wish to have a look at this cool feature, go and grab the SVN repository on google: http://code.google.com/p/yii/source/checkout
Tweet this!
A sidebar marker trigger with EGMap 2.0
0Introduction
This is another article from a feature requested by a EGMap Yii Extension User. He proposed me when the following will be incorporated to the library: http://gmaps-samples-v3.googlecode.com/svn/trunk/sidebar/random-markers.html.
I am going to demonstrate that the extension is already capable of creating that without the need of more ‘library tweaking’.
HTML and Styling
First of all we are going to write the CSS and the HTML that will ‘mimic’ the example provided in the previous link. As you are going to see, there is also a JavaScript helper function that will handle the creation of LI elements (as in the example).
<style>
#sideContainer {
list-style-type: none;
padding: 0;
margin: 0 10px 0 0;
float: left;
border: 1px solid #676767;
background-color: #eee;
overflow: auto;
}
#sideContainer li {
font-size: 0.9em;
border-bottom: 1px solid #aaa;
padding: 5px;
}
#mapContainer {
float: left;
width: 500px;
height: 400px;
}
</style>
</head>
<script>
// global marker counter
var n = 1;
function generateListElement( marker ){
var ul = document.getElementById('sideContainer');
var li = document.createElement('li');
var aSel = document.createElement('a');
aSel.href = 'javascript:void(0);';
aSel.innerHTML = 'Open Marker #' + n++;
aSel.onclick = function(){ google.maps.event.trigger(marker, 'click')};
li.appendChild(aSel);
ul.appendChild(li);
}
</script>
<body>
<!-- the side menu container -->
<ul id="sideContainer" style></ul>
<!-- we are going to render the map here -->
<div id="mapContainer"></div>
Creating the Map
For the sake of this example, we are going to create just one EGMapInfoWindow object and two markers. The most important thing is to demonstrate how to use callbackTriggers with EGMap 2.0. As you will now see, it is pretty easy to do.
// array holding a reference to all the markers
// that will be rendered to the Map
$markers = array();
$gMap = new EGMap();
$gMap->zoom = 10;
$gMap->setCenter('39.721089311812094', '2.91165944519042');
// Create GMapInfoWindow
$info_window_b = new EGMapInfoWindow('Hey! I am a marker with label!');
// Create 1st marker
$marker = new EGMapMarker(39.721089311812094, 2.91165944519042, array('title' => 'Marker With Label'));
// attach info window
$marker->addHtmlInfoWindow($info_window_b);
// add to map
$gMap->addMarker($marker);
// add to array
$markers[] = $marker;
// repeat process with second
$marker = new EGMapMarker(39.721089311812094, 2.81165944519042, array('title' => 'Marker With Label'));
$marker->addHtmlInfoWindow($info_window_b);
$gMap->addMarker($marker);
$markers[] = $marker;
// tell the map we want to render it
// to a specific layer
$gMap->appendMapTo('#mapContainer');
// initialize the afterInit array that
// will hold after map initialization
// script code
$afterInit = array();
//
// loop through markers and
// call global function to generate
// the element that will hold the
// callback trigger event
foreach($markers as $marker){
$afterInit[] = 'generateListElement('.$marker->getJsName().');'.PHP_EOL;
}
// now render map and pass the afterInit code
$gMap->renderMap($afterInit);
Final Words
The above code is very simplistic, if we were to render lots of markers to the map, a better approach would be to make that on a loop and even more, create a couple of functions to simplify the creation of the markers.
Hope this example helps you guys to better understand the flexibility of this extension. Thanks all for using it.
Tweet this!
A Reverse Geolocator Tool with EGMap 2.0 Extension
1
I have been requested to create an article about a reverse geolocator tool, that is a tool to find out the latitude and longitude of a location, to include on our CMS, and here it is.
Styling, Javascript and HTML
First of all, we are going to write the HTML that will work with this example, it won’t styled as the example picture displayed, which is the tool I created for a project I am working now, but don’t you worry as this article will provide you with the scripts and routines to create your own.
Write the following style on the HEAD section of your HTML page:
<style>
div#map {
position: relative;
}
div#crosshair {
position: absolute;
/*
the top will be half of the width of the map
less 50% of its size more or less
to center the image correctly on the map
*/
top: 192px;
height: 19px;
width: 19px;
left: 50%;
margin-left: -8px;
display: block;
/* we are going to borrow a crosshair gif from google */
background: url(http://gmaps-samples-v3.googlecode.com/svn/trunk/geocoder/crosshair.gif);
background-position: center center;
background-repeat: no-repeat;
}
</style>
Now, some Javascript functions that will allow us to get the information from the map
<script type="text/javascript">
//
// function to get the latitude and longitude
// and place them on the test fields
function setLatLngToClass(){
if(document.getElementById('test_latitude'))
document.getElementById('test_latitude').value = map.getCenter().lat();
if(document.getElementById('test_longitude'))
document.getElementById('test_longitude').value = map.getCenter().lng();
}
//
// function to get Centered Latitude and Longitude points
function getCenterLatLngText() {
return '(' + map.getCenter().lat() +', '+ map.getCenter().lng() +')';
}
//
// function to call when the center of the map
// has changed. Center information will be
// collected and displayed on the document
// elements
function centerChanged() {
centerChangedLast = new Date();
var latlng = getCenterLatLngText();
document.getElementById('latlng').innerHTML = latlng;
document.getElementById('formatedAddress').innerHTML = '';
currentReverseGeocodeResponse = null;
}
//
// Collects reverse center location
function reverseGeocode() {
reverseGeocodedLast = new Date();
geocoder.geocode({latLng:map.getCenter()},reverseGeocodeResult);
}
//
// Displays collected reverse geocoded results
// and displays them on document elements
function reverseGeocodeResult(results, status) {
currentReverseGeocodeResponse = results;
if(status == 'OK') {
if(results.length == 0) {
document.getElementById('formatedAddress').innerHTML = 'None';
} else {
document.getElementById('formatedAddress').innerHTML = results[0].formatted_address;
}
} else {
document.getElementById('formatedAddress').innerHTML = 'Error';
}
}
//
// geocodes the address inserted
function geocode() {
var address = document.getElementById("address").value;
geocoder.geocode({
'address': address,
'partialmatch': true}, geocodeResult);
}
function geocodeResult(results, status) {
if (status == 'OK' && results.length > 0) {
map.fitBounds(results[0].geometry.viewport);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
}
//
// adds marker to the center of the map
function addMarkerAtCenter() {
var marker = new google.maps.Marker({
position: map.getCenter(),
map: map
});
var text = 'Lat/Lng: ' + getCenterLatLngText();
if(currentReverseGeocodeResponse) {
var addr = '';
if(currentReverseGeocodeResponse.size == 0) {
addr = 'None';
} else {
addr = currentReverseGeocodeResponse[0].formatted_address;
}
text = text + '<br>' + 'address: <br>' + addr;
}
var infowindow = new google.maps.InfoWindow({ content: text });
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(map,marker);
});
}
</script>
Our HTML on this example will be the following one:
<body style="background:white">
<div class="form">
Find by address:
<input type="text" id="address" style="width:300px"/>
<button type="button" class="small"onclick="geocode()">Go to Address</button>
<ul>
<li>Lat/Lng: <span id="latlng"></span></li>
<li>Address: <span id="formatedAddress"></span></li>
<li>Zoom Level: <span id="zoom_level"><?php echo $zoom;?></span></li>
</ul>
</div>
<div id="map">
<div id="map_canvas" style="width:100%; height:400px"></div>
<div id="crosshair"></div>
</div>
<div style="overflow:hidden;width:100%;text-align:right">
<button type="button" class="small" onclick="setLatLngToClass()">Set Latitude & Longitude</button>
<button type="button" class="small" onclick="addMarkerAtCenter()">Add Marker at Center</button>
</div>
<hr>
Latitude: <input id="test_latitude" value=""/> Longitude: <input id="test_longitude" value=""/>
</hr>
</body>
Using EGMap 2.0 Extension
Finally, we are going to use EGMap 2.0 extension to automate the rest of the tasks to render our map.
Yii::import('ext.gmaps.*');
// center the map
// wherever you want
$latitude = 39.72098197183251;
$longitude = 2.9115524999999964;
$zoom = 8;
$gMap = new EGMap();
$gMap->setJsName('map');
$gMap->width = '100%';
$gMap->height = '400';
$gMap->setCenter($latitude, $longitude);
$gMap->zoom = 8;
$gMap->addGlobalVariable('geocoder');
$gMap->addGlobalVariable('centerChangedLast');
$gMap->addGlobalVariable('reverseGeocodedLast');
$gMap->addGlobalVariable('currentReversGeocodeResponse');
$gMap->addEvent(
new EGMapEvent(
'zoom_changed',
'document.getElementById("zoom_level").innerHTML = map.getZoom();'));
$gMap->addEvent(new EGMapEvent('center_changed','centerChanged',false));
$gEvent = new EGMapEvent('dblclick','map.setZoom(map.getZoom() +1)');
$gMap->appendMapTo('#map_canvas');
$gMap->renderMap(array(
'geocoder = new google.maps.Geocoder();',
$gEvent->getDomEventJs('crosshair'),
'reverseGeocodedLast= new Date();',
'centerChagedLast = new Date();',
'setInterval(function(){
if((new Date()).getSeconds() - centerChangedLast.getSeconds() > 1) {
if(reverseGeocodedLast.getTime() < centerChangedLast.getTime())
reverseGeocode();
}
},1000);',
'centerChanged();'
));
Important
If you are going to run this example, please be aware that in order to display it properly in a controller, all of the above have to be the content of a layout, otherwise, if you are using renderPartial (that you can), make sure you force to true the parameter ‘processOutput’ of the mentioned function (ie $this->renderPartial(‘view’,null,false,true) )
Tweet this!
EFeed Universal RSS Feed Writer For Yii
0Introduction
Required for one of my projects, I decided to develop my own Yii extension to create RSS Feeds. I knew there is already one but I wanted something easier to use than that. This is why I came up with EFeed Extension. This extension supports RSS 1.0, RSS 2.0, and ATOM 1.0 standards.
How to Use
I assume that you have downloaded the extension and place it on your protected/extensions folder.
RSS 1.0 Example
Yii::import('ext.feed.*');
// specify feed type
$feed = new EFeed(EFeed::RSS1);
$feed->title = 'Testing the RSS 1 EFeed class';
$feed->link = 'http://www.ramirezcobos.com';
$feed->description = 'This is test of creating a RSS 1.0 feed by Universal Feed Writer';
$feed->RSS1ChannelAbout = 'http://www.ramirezcobos.com/about';
// create our item
$item = $feed->createNewItem();
$item->title = 'The first feed';
$item->link = 'http://www.yiiframework.com';
$item->date = time();
$item->description = 'Amaz-ii-ng <b>Yii Framework</b>';
$item->addTag('dc:subject', 'Subject Testing');
// add it to the feed
$feed->addItem($item);
$feed->generateFeed();
As you can see in the example above, we just need to create items and add them to the feed. The example could be easily modified to add items extracted from a database and add them to the Feed in a loop.
RSS 2.0 Example
Yii::import('ext.feed.*');
// RSS 2.0 is the default type
$feed = new EFeed();
$feed->title= 'Testing RSS 2.0 EFeed class';
$feed->description = 'This is test of creating a RSS 2.0 Feed';
$feed->setImage(
'Testing the EFeed class',
'http://www.ramirezcobos.com',
'http://www.yiiframework.com/forum/uploads/profile/photo-7106.jpg');
$feed->addChannelTag('language', 'en-us');
$feed->addChannelTag('pubDate', date(DATE_RSS, time()));
$item = $feed->createNewItem();
$item->title = "first Feed";
$item->link = "http://www.yahoo.com";
$item->date = time();
$item->description = 'This is test of adding ' .
'CDATA Encoded description <b>EFeed Extension</b>';
// this is just a test!!
$item->setEncloser(
'http://www.tester.com',
'1283629', 'audio/mpeg');
$item->addTag(
'author',
'thisisnot@myemail.com (Antonio Ramirez)');
$item->addTag(
'guid',
'http://www.ramirezcobos.com',
array('isPermaLink'=>'true'));
$feed->addItem($item);
$feed->generateFeed();
ATOM 1.0 Example
Yii::import('ext.feed.*');
$feed = new EFeed(EFeed::ATOM);
// IMPORTANT : No need to add id for feed or channel.
// It will be automatically created from link.
$feed->title = 'Testing the ATOM RSS EFeed class';
$feed->link = 'http://www.ramirezcobos.com';
$feed->addChannelTag('updated', date(DATE_ATOM, time()));
$feed->addChannelTag('author', array('name'=>'Antonio Ramirez Cobos'));
$item = $feed->createNewItem();
$item->title = 'The first Feed';
$item->link = 'http://www.ramirezcobos.com';
// we can also insert well formatted date strings
$item->date ='2010/24/12';
$item->description = 'Test of CDATA Encoded description <b>EFeed Extension</b>';
$feed->addItem($item);
$feed->generateFeed();
Download
To download the extension go to Yii’s extension repository. Hope this little piece of code can help your project needs somehow.
Tweet this!
Paths, Paths, Yii Paths!
8Introduction
When we start learning something and we go through some parts, we completely forget about what was a small issue in our way to get where we are. I started talking about things that can be hard to understand if the initial concepts are not well understood. For this reason, even though for some this small tutorials are too basic, I would like to post them every now and then to help newcomers to better understand the basic concepts of the articles.
Paths
Before, you continue with this article, I assume that you have read the fundamentals of Path Aliasing and Namespaces from the Definitive Guide of Yii
We are going to talk about paths and before we start, we should first explain how Yii creates its folder structure after you use its yiic tool. This is how it looks and the definitions of the folders (assuming you have created an application called testweb):
testweb/ containing your web
assets/ containing published resource files
css/ containing CSS files
images/ containing image files
themes/ containing application themes
protected/ containing protected application files
commands/ containing customized 'yiic' commands
shell/ containing customized 'yiic shell' commands
components/ containing reusable user components authentication
config/ containing configuration files
controllers/ containing controller class files
data/ containing the sample database
extensions/ containing third-party extensions
messages/ containing translated messages
models/ containing model class files
runtime/ containing temporarily generated files
tests/ containing test scripts
views/ containing controller view and layout files
layouts/ containing layout view files
site/ containing view files for the 'site' controller
pages/ containing "static" pages
Quite impressive, Yii MVC folder structure is very well created and for simple projects, we probably don’t need to change it, but if we wish to do it, or we create some widgets or portlets or components or whatever, and those need to access different folder locations in our application, Yii paths related functions come to resue:
So, tell me, how do they work
A path alias is ‘nickname’ that we give to a folder in our Web project. Yii comes with five of them already established.
system: refers to the Yii framework directory;zii: refers to the Zii library directory;application: refers to the application’s base directory;webroot: refers to the directory containing the entry script file. This alias has been available since version 1.0.3.ext: refers to the directory containing all third-party extensions. This alias has been available since version 1.0.8.
So, in our example above, these are the correspondent translations:
Yii::getPathOfAlias(‘webroot.images’) is equal to the translated images folder path on your computer or server (ie. windows: C:\mywebserverpath\testweb\images, linux: /usr/web/www/testweb/images). I do use this feature to save images or folders on a folder when I develop CMS for my clients. Here is an example:
$picture_file = CUploadedFile::getInstanceByName('Filedata');
//...
//... some validation code here
//...
// create a picture name
$picture_name = Picture::createPictureName($picture_file->name);
// saving file
// 'othersubfolder' is just an imaginary subfolder under images
$picture_file->saveAs(
Yii::getPathOfAlias('webroot.images.othersubfolder') .
DIRECTORY_SEPARATOR .
$picture_name );
Now, lets imagine that I wish to create a new folder 2010 for my application and it is located very far down on the folder structure and I wish to access it in order to save documents there. Here is the example:
testweb/
assets/
css/
docs/
clients/
2010/
January/
To make our life easier, we are going to use Yii::setPathOfAlias to provide an alias to the 2010 folder. For the sake of the example we are going to configure it in our index.php file after the creation of our Application:
defined('DS') or define('DS',DIRECTORY_SEPARATOR);
Yii::createWebApplication($config)->run();
Yii::setPathOfAlias('2010',
dirname($_SERVER['SCRIPT_FILENAME']).DS.
'docs'.DS.
'clients'.DS.'2010');
// Now we can access the folder to read and/or write like this:
$pathToDocs = Yii::getPathOfAlias('2010');
Remember, than setPathOfAlias doesn’t normalizes the path and getPathOfAlias do not check for the existence of the folder just check if the alias exist. We need to put special attention to them.
And what about import?
I wouldn’t explain it better than Yii:
Using aliases, it is very convenient to include the definition of a class. For example, if we want to include theCController class, we can call the following:
Yii::import('system.web.CController');
The import method differs from include and require in that it is more efficient. The class definition being imported is actually not included until it is referenced for the first time (implemented via PHP autoloading mechanism). Importing the same namespace multiple times is also much faster than include_once and require_once.
We can also use the following syntax to import a whole directory so that the class files under the directory can be automatically included when needed.
Yii::import('system.web.*');
Besides import, aliases are also used in many other places to refer to classes. For example, an alias can be passed to Yii::createComponent() to create an instance of the corresponding class, even if the class file was not included previously.
Further Reading
We have covered just a small portion of the great funcionality of Yii. I highly recommend you to also look at the following path related functions:
- getBasePath -Returns the root path of the application. Your root URL.
- setBasePath -Sets the root directory of the application. This method can only be invoked at the begin of the constructor.
- getExtensionPath – Returns the root directory that holds all third-party extensions.
- setExtensionPath – Sets the root directory that holds all third-party extensions.
- setRuntimePath – Sets the directory that stores runtime files.
- getRuntimePath – Returns the root directory that stores runtime files
- getLocaleDataPath – Returns the directory that contains the locale data. It defaults to ‘framework/i18n/data’.
- setLocaleDataPath – Sets the directory that contains the locale data.
Hope this helps new comers to better understand the concepts working with path Yii way.
Tweet this!
More than one Instance of SWFUpload Yii Extension
4
Requested by a reader, I promised to him to write an article describing how to have more than one instance of the great Yii extension SWFUpload. Even though he has fixed the issue by himself, I will describe how to do it for others to know just in case they face the same challenge.
Step One Fixing Internal Extension Code
If we look at the extension code we will see that the problem is, in fact, due to the nature of it. Lets look at the code, I will comment where the problem relies:
public function run()
{
$assets = dirname(__FILE__).'/swfupload';
$baseUrl = Yii::app()->assetManager->publish($assets);
$cs = Yii::app()->getClientScript();
// Registering required SWFUpload files
$cs->registerScript(__CLASS__.'swfuv',"var swfuPath='" . $baseUrl . "';", CClientScript::POS_HEAD);
$cs->registerScriptFile($baseUrl . '/swfupload.js', CClientScript::POS_HEAD);
$cs->registerScriptFile($baseUrl . '/handlers.js', CClientScript::POS_END);
$cs->registerCssFile($baseUrl . '/swfupload.css');
// ensure PHPSESSIONID for SWFUpload session ID problem
$postParams = array('PHPSESSID'=>session_id());
// if POST parameters have been set by user, merge to the configuration
// post_params parameter of SWFUpload
if(isset($this->postParams))
$postParams = array_merge($postParams, $this->postParams);
// default config options
$config = array(
'post_params'=> $postParams,
'flash_url'=> $baseUrl. '/swfupload.swf',
'button_image_url'=>
$baseUrl .'/images/SmallSpyGlassWithTransperancy_17x18.png',
);
$config = array_merge($config, $this->config);
$config = CJavaScript::encode($config);
// here is where the problem relies, we only register one script
// by one variable javascript name, we need to modify this.
$cs->registerScript(__CLASS__, "
var swfu;
swfu = new SWFUpload($config);
");
}
Now that we have found where the issue is, we just need to change a bit the code to be able to work with more than one SWFUpload buttons at once. First, we are going to add a static counter to the extension, and it will be the one that will make sure that each instance will have its own name. And then, we will provide a unique name to the script that is flushed to the response. Also, even though Yii handles it very well and makes sure no registered script under the same name is twice register, we will check if required files are already registered by CClientScript -by using the isScriptRegistered, isScriptFileRegistered and isCssFileRegistered functions. Following my proposed solution:
class CSwfUpload extends CWidget
{
public $postParams=array();
public $config=array();
// static counter to avoid instances
// name conflict
private static $_counter=0;
public function run()
{
$assets = dirname(__FILE__).'/swfupload';
$baseUrl = Yii::app()->assetManager->publish($assets);
$cs = Yii::app()->getClientScript();
// isScriptRegistered makes sure we dont register the script twice
if(!$cs->isScriptFileRegistered(__CLASS__.'swfuv', CClientScript::POS_HEAD))
$cs->registerScript(__CLASS__.'swfuv',"var swfuPath='" . $baseUrl . "';", CClientScript::POS_HEAD);
if(!$cs->isScriptFileRegistered($baseUrl . '/swfupload.js', CClientScript::POS_HEAD))
$cs->registerScriptFile($baseUrl . '/swfupload.js', CClientScript::POS_HEAD);
if(!$cs->isScriptFileRegistered($baseUrl . '/handlers.js', CClientScript::POS_END))
$cs->registerScriptFile($baseUrl . '/handlers.js', CClientScript::POS_END);
if(!$cs->isCssFileRegistered($baseUrl . '/swfupload.css'))
$cs->registerCssFile($baseUrl . '/swfupload.css');
$postParams = array('PHPSESSID'=>session_id());
if(isset($this->postParams))
{
$postParams = array_merge($postParams, $this->postParams);
}
$config = array(
'post_params'=> $postParams,
'flash_url'=> $baseUrl. '/swfupload.swf',
'button_image_url'=>
$baseUrl .'/images/SmallSpyGlassWithTransperancy_17x18.png',
);
$config = array_merge($config, $this->config);
$config = CJavaScript::encode($config);
// update our static variable in order to create
// a unique variable javascript name
self::$_counter++;
// we could just use the same static variable to
// register the script name but I thought this
// is not bad solution to show
// PLEASE SEE THE USE OF THE STATIC VARIABLE
// TO CREATE THE SWFUPLOAD OBJECT
$cs->registerScript(__CLASS__.sha1(self::$_counter), "
var swfu".self::$_counter." = new SWFUpload($config);
");
}
}
Step Two Modifying our View
Now, last but not least, for our modified extension to work we need to setup our view with the correct settings. We will have to create two widgets, and they will have to point to two different layers to render the SWFUpload object -please refer to the article on How to use SWFUpload Extension on this blog for instructions with one instance.
Following, an example on how to declare those two instances and the HTML:
// where are we going to post?
$uurl = $this->createUrl('picture/upload',array('newsId'=>$newsId));
$options = array(
'use_query_string'=>false,
'upload_url'=> CHtml::normalizeUrl($uurl),
'file_size_limit'=>'2 gb',
'file_types'=>'*.jpg;*.png;*.gif',
'file_types_description'=>'Imagenes',
'file_upload_limit'=>0,
'file_queue_error_handler'=>'js:fileQueueError',
'file_dialog_complete_handler'=>'js:fileDialogComplete',
'upload_progress_handler'=>'js:uploadProgress',
'upload_error_handler'=>'js:uploadError',
'upload_success_handler'=>'js:uploadSuccess',
'upload_complete_handler'=>'js:uploadComplete',
// where are we going to inform our upload progress
// for this SWFUpload object?
'custom_settings'=>array('upload_target'=>'divFileProgressContainer'),
// where are we going to render the SWFUpload button?
'button_placeholder_id'=>'swfupload',
'button_width'=>230,
'button_height'=>20,
'button_text'=>'<span class="button">Select Images (2 MB Max)</span>',
'button_text_style'=>'.button { font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; font-size: 11pt; text-align: center; }',
'button_text_top_padding'=>0,
'button_text_left_padding'=>0,
'button_window_mode'=>'js:SWFUpload.WINDOW_MODE.TRANSPARENT',
'button_cursor'=>'js:SWFUpload.CURSOR.HAND',
);
// creeate one widget!
$this->widget('application.widgets.CSwfUpload', array(
'postParams'=>array('newsId'=>$newsId),
'config'=>$options,
)
);
// change options for our next uploader
$options['custom_settings']=array('upload_target'=>'divFileProgressContainer2');
$options['button_placeholder_id']='swfupload2';
// create new widget (I dont change the post params
// nor the post URL that handles submission because
// it is not important for the sake of the example
$this->widget('application.widgets.CSwfUpload', array(
'postParams'=>array('newsId'=>$newsId),
'config'=>$options,
)
);
Here our HTML, please refer to the code above to see how the layers are connected to the options of the plugin.
<div class="container">
<div id="content">
<h2>Image Uploader</h2>
<p>Click at the following button to upload the images of the news gallery.
All images will be resized automatically.</p>
<form>
<div class="form">
<div class="row">
<div class="swfupload" style="display: inline; border: solid 1px #7FAAFF; background-color: #C5D9FF; padding: 2px;">
<span id="swfupload"></span>
</div>
<div id="divFileProgressContainer" style="height: 75px;"></div>
</div>
<div class="row">
<div class="swfupload" style="display: inline; border: solid 1px #7FAAFF; background-color: #C5D9FF; padding: 2px;">
<span id="swfupload2"></span></div>
<div id="divFileProgressContainer2" style="height: 75px;"></div>
</div>
<div id="thumbnails"></div>
</div>
</form>
</div><!-- content -->
</div>
Addedum
I highly recommend you to have a look at the code of every extension you download. They are a good place to learn and to also help us improve our ‘problem solving’ thinking. I hope that you enjoyed this article as much as I had writing it.
Tweet this!
EGMap Google Maps Extension for Yii
10Introduction
I had to develop a Google maps extension for my current personal project and I did some research in order not to re-invent the wheel.
The best of them all was a Symphony plugin developed by Fabrice Bernhard. I didn’t want to create a wrapper so I decided to modify its code -in some cases rewriting the whole class, in order to create a full Extension built with Yii.
The features of this extension are very large to explain, therefore I will concentrate my next posts to fully explain by example the wonders of this extension.
Requirements
Developed with latest stable version of Yii (1.5)
Usage
Unzip its contents and place the gmaps folder into your protected/extensions folder.
A Reallly Easy Example
Displaying a Marker with an Info window.
Note: This example was written in a view file.
// import the library
Yii::import('ext.gmaps.*');
$gMap = new EGMap();
$gMap->setZoom(13);
$gMap->setCenter(39.721089311812094, 2.91165944519042);
// Create GMapInfoWindow
$info_window = new EGMapInfoWindow('<div>I was living here as a kid!</div>');
// Create marker
$marker = new EGMapMarker(39.721089311812094, 2.91165944519042, array('title' => '"My Town"'));
$marker->addHtmlInfoWindow($info_window);
$gMap->addMarker($marker);
$gMap->renderMap();
Resources
Download the Extension
Bug Reports & Positive Feedback
Tweet this!
How to maintain pages on CGridView
2Introduction
Lots of Yii forum members have ask how to return to current navigated page on a CGridView after a user has clicked the update or create button. Meaning that if I am in page number 4 of our Grid and we have clicked on the ‘edit’ button, how, and after I clicked the submit button return to the page number 4 as our Grid was?
Through this small tutorial on how to get current page parameter, you will learn how to do exactly that. At least I hope so.
Bit of analysis
If you check at the parameters of the Yii pagination classes you will see that the parameter name of the pagination is on the format of: Modelname_page. So, if we are at our NewsController admin page, we will surely see the pagination as URL?&News_page=2 for example. Now that we know that we are going to tweak a bit our code to accomplish what we want.
Modifying CButtonColumn
CButtonColumn is the class that takes care of the rendering of our edition buttons (view, update, delete), we are going to modify its buttons URL. How to do it? Well, as I always said, it is really good to look at the guts of Yii framework; if we check the CButtonColumn class code we will see that this class has three properties that we can set: viewButtonUrl, updateButtonUrl and deleteButtonUrl. A closer look to these variables we see:
public $viewButtonUrl='Yii::app()->controller->createUrl("view",array("id"=>$data->primaryKey))';
public $updateButtonUrl='Yii::app()->controller->createUrl("update",array("id"=>$data->primaryKey))';
public $deleteButtonUrl='Yii::app()->controller->createUrl("delete",array("id"=>$data->primaryKey))';
These variables have PHP code that is afterwards evaluated in a very smart way. So, as you probably know by now, we need to modify that code in the view where we render the grid to include our new links, the ones that will include the current page parameter for our Grid.
// setting up the class
$buttons = array('class'=>'CButtonColumn');
// do we have a News_page parameter?
$page = Yii::app()->getRequest()->getParam('News_page',false);
// if yes, modify the link
// IMPORTANT
// for this example I assume that you have
// and update, view and delete actions!
if( $page ){
foreach(array('view','update','delete') as $id){
$buttons[$id.'ButtonUrl'] = 'Yii::app()->controller->createUrl("'.$id.'",array("id"=>$data->primaryKey,"News_page"=>'.$page.'));';
}
}
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'news-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
array(
'name'=>'id',
'filter'=>false
),
'title',
'sender',
array(
'name'=>'datetime_sent',
'type'=>'datetime',
'filter'=>false,
),
// HERE WE PLACE OUR NEW BUTTONS
$buttons,
),
));
Exercise
Now our edition buttons will hold also the current page parameter of our News model. That means that when we render the corresponding views for viewing, editing or even calling actionDelete of our controller, we will also render that parameter.
I think that it could be a good exercise for you to find out how deal with that parameter to return to the page you were after editing your model. I will give you two quick tips:
On my _Form.php view
if(getParam('Brand_page')){
echo CHtml::hiddenField('Brand_page',getParam('Brand_page'));
}
On my actionUpdate in NewsController
public function actionUpdate($id)
{
$model=$this->loadModel($id);
if(isset($_POST['News']))
{
$model->attributes=$_POST['News'];
$page = Yii::app()->getRequest()->getParam('News_page',false);
$params = $page? array('News_page'=>$page) : array();
if($model->save()){
// redirecting to admin.php view where our grid is
$route = $this->createUrl('admin',$params);
$this->redirect($route,true);
}
}
$this->renderPartial('_form',array(
'model'=>$model,
));
}
Further Reading
Quick tip about pagination params
Using CButtonColumn to customize buttons in CGridView
Tweet this!
Real Estate Colonia San Pedro
10
Real Estate Web Application fully developed with Yii Framework.
To visit the site: http://www.mallorcacolonia.com/
Website Features
- Multiple language support
- Prettyphoto Plugin
- JQuerySlider Menu Plugin
- AnythingSlider Plugin
- Fancybox Plugin
- Instant Notification Support
- reCaptcha
Web Panel Features
- The webpanel allows to manage:
- Web Translations
- Property Types
- Properties (Sale, Rent, Inactive) – Gallery – Property Owners
- Zones
- Site Languages
- Inbox
Backend Gallery
Tweet this! 












Try to register and click again. Sometimes the plugin i have to count downloads do fail.There was an issue with the cache, solved by […] 6 days ago