martes, 9 de agosto de 2011

My First Symfony2 project with MS SQL Server 2008

A couple of days ago Symfony2 was finally released. I had been waiting for it because it promised to work with MS SQL Server 2008 via Doctrine2. I know what you're thinking why use SQL Server? Well let's just say that it's a job requirement.


This post attempts to replicate the example show on the Symfony site for Interactive Generators on Windows with a couple of twists. First I attempt to use MS SQL Server 2008 and add complex relationship between Posts and Tags as proposed on http://www.dobervich.com/2011/03/05/symfony2-blog-application-tutorial-part-ii-the-data-model/ .


Installation


I´ll be using Wamp 2.0 with PHP 5.3.6 (to learn how to configure PHP 5.3.6 with Wamp read http://markushedlund.com/dev-tech/install-php-5.3.3-on-wamp ) and MS SQL Server 2008 R2 (Express).


Download Simfony2.0.0 zip file.


Make sure php.exe is in your path variable.


Unzip the content of the zip file into your netbeans project directory (C:\NetbeansProjects for me).


Rename the Symfony directory to acme_blog.


Open a command window and move to the acme_blog directory.


Run php .\app\console --version.


You should get the version of Symfony as shown:




201108092141.jpg



Generating a Blog Bundle


On the command window run:


php .\app\console generate:bundle


Fill the data as follows:


Bundle namespace: Acme/BlogBundle


Bundle name[AcmeBlogBundle]:


Target directory [C:\NetbeansProjects\acme_blog/src]:


Configuration format (yml, xml, php, or annotation) [annotation]:




201108092143.jpg



Do you want to generate the whole directory structure [no]?


Do you confirm generation [yes]?


Confirm automatic update of your Kernel [yes]?


Confirm automatic update of the Routing [yes]?




201108092144.jpg



Opening the Project with Netbeans 7.0


Open Netbeans select File\New Project…


Select PHP Application with Existing Sources.




201108092145.jpg



Click Next.


Select the source folder where you unzipped the Symfony framework.


Select PHP Version: 5.3




201108092146.jpg



Click Next.





201108092146.jpg


Click Next.


On the Projects tabe you should be able to see the BlogBundle.




201108092147.jpg



Generating the Doctrine Entities


All the magic of Symfony 1.4.x where you created your schema con your schema.yml file and with a build --all command you would get your entities and database created for you are gone.


With Symfony 2 you use the gen:doctrine:entity command to dynamically create an entity.


Post Entity


We are going to create an entity name Post, that has three fields title (String (255)), body (String(255)) and published_at (datetime).


On a command window type:


php .\app\console gen:doctrine:entity


The Entity shortcut name: AcmeBlogBundle:Post


Add the three fields as shown:




201108092147.jpg



Do you want to generate an empty repository class [no]?


Do you confirm generation [yes]?


Look for the entity in your Netbeans Project under acme_blog\src\Acme\BlogBundle\Entity\Post.php




201108092148.jpg



Tag Entity


We are going to create an entity name Tag, that has two fields name (String (255)) and created_at (datetime).


On a command window type:


php .\app\console gen:doctrine:entity


The Entity shortcut name: AcmeBlogBundle:Tag


Add the two fields as shown:




201108092149.jpg



Manually Create the Many to Many Relationship


Open the Post.php file and add the following code at the beginning


use Doctrine\Common\Collections\ArrayCollection;


Under the published at field add the following code:


/**


* @ORM\ManyToMany(targetEntity="Tag")


* @ORM\JoinTable(name="post_tag", joinColumns={@ORM\JoinColumn(name="post_id", referencedColumnName="id")}, inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")} )


* @var ArrayCollection $tags


*/


private $tags;


….


public function getTags(){


return $this->tags;


}


public function setTags($tags){


$this->tags =$tags;


}


Overriding the __ToString() function on Tags


public function __toString(){


return $this->name;


}


Create a Database


Start Wamp.


Create the Database Manually


Open Microsoft SQL Server Managemente Studio.


Right click con Databases and select New Database.


On the New Database window. Type acme_blog on the Database name and select sa as Owner.





Edit the parameters.ini


Edit the Source Files\app\config\parameters.ini


Change the database_name to acme_blog, database_host to your instance, database_user to sa and database_password to your sa password.




201108092155.jpg



On a command window type:


php .\app\console doctrine:schema:create


This will create the table Post, Tag and post_tag.


Generating CRUD Controllers and Templates


On a command window type:


php .\app\console doctrine:generate:crud


The Entity shortcut name: AcmeBlogBundle:Post


Do you want to generate the "write" actions [no]? y


Configuration format (yml, xml, php, or annotation) [annotation]:


Routes prefix [/post]:




201108092156.jpg



Do you confirm generation [yes]?




201108092156.jpg



This action creates the Views and Forms as well as the entries in the PostController.



Repeat the process for the Tag entity.




201108092157.jpg


Testing the Bundle


Click on the Wamp icon and click on Apache\Alias directories\Add an alias.





201108092157.jpg


Use acme_blog as alias and point to C:\NetbeansProjects\acme_blog.





201108092158.jpg


Access the url http://localhost/acme_blog/web/app_dev.php/post/. You should be able to add a Post.




201108092158.jpg




sábado, 30 de julio de 2011

Using BETWEEN syntax with Symfony 1.4.x and MS SQL Server 2008

I´m using Symfony 1.4.8 and MS SQL Server 2008.

I had a query that read:



$q = Doctrine_Query::create()
->select('m.mapdate, m.created_at')
->from('GISMap m')
->where('m.mapdate BETWEEN ? AND ?', array($start_date, $end_date))
->orderBy('m.mapdate DESC');

This worked perfectly in MySQL, but when I switched to MS SQL Server I got an error like this one:



500 | Internal Server Error | Doctrine_Connection_Mssql_Exception



SQLSTATE[HY000]: General error: 10007 Incorrect syntax near '?'. [10007] (severity 5) [SELECT [g].[id] AS [g__id], [g].[mapdate] AS [g__mapdate], [g].[created_at] AS [g__created_at] FROM [gis_map] [g] WHERE ([g].[mapdate] BETWEEN '2010-03-24 10:32:24' AND ?) ORDER BY [g].[mapdate] DESC]. Failing Query: "SELECT [g].[id] AS [g__id], [g].[mapdate] AS [g__mapdate], [g].[created_at] AS [g__created_at] FROM [gis_map] [g] WHERE ([g].[mapdate] BETWEEN '2010-03-24 10:32:24' AND ?) ORDER BY [g].[mapdate] DESC"



After doing some research I found out that the replacement of the ? for the actual values is done by the PDO driver. So I decided that the problem was the driver for SQL Server didn´t like the array being passed to it.



I change the syntax to:

$q = Doctrine_Query::create()
->select('m.mapdate, m.created_at')
->from('GISMap m')
->where('m.mapdate >= ?', $start_date)
->andWhere('m.mapdate <= ?', $end_date)
->orderBy('m.mapdate DESC');

Now it works. It´s annoying to have to do this, but it´s been my experience that working with SQL Server will get you into these troubles.






Maven Error: Could not calculate build plan

I recently had to change computre and had some trouble with Maven.

I got the following message on Eclipse 3.5.2:

Could not calculate build plan: Failure to transfer org.apache.maven.plugins:maven-resources-plugin:pom:2.4.3 from http://repo1.maven.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced. Original error: Could not transfer artifact org.apache.maven.plugins:maven-resources-plugin:pom:2.4.3 from/to central (http://repo1.maven.org/maven2): Failed to transfer http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.4.3/maven-resources-plugin-2.4.3.pom. Error code 400, Bad Request jsimpleutils Unknown Maven Problem

On the following post http://vikashazrati.wordpress.com/2010/06/26/could-not-calculate-build-plan-missing-maven-resources-pluginmaven-plugin2-4-1/ a guy pointed out the he could find the directory but it had a filed with the extension .lastUpdated.

The solution was to delete the file with the .lastUpdated extension (maven-resources-plugin-2.4.1.maven-plugin.lastUpdated) and ran from eclipse maven->Update Project Configuration and the problem was resolved.






sábado, 23 de julio de 2011

Using IIS 7.5 with URL Rewrite and Symfony

The IIS URL Rewrite 2.0 is IIS module that promises to do what the mod_rewrite does for Apache.


Download the URL Rewrite module from http://www.iis.net/download/urlrewrite .


Installation


Install the rewrite_2.0_rtw_x32.msi (or rewrite_2.0_rtw_x64.msi if you´re using a 64bit OS).


201107231141.jpg


201107231142.jpg


Check the the fastcgi is properly configured in your php.ini. This is not part of the installation of the URL Rewrite module but I had to do it because I changed from PHP 5.3 to 5.2 (due to the reasons explained at http://tecnofuenteabierta.blogspot.com/2010/12/running-symfony-14-with-ms-sql-server.html ).


cgi.force_redirect=0


fastcgi.impersonate=1


fastcgi.logging=0


extension_dir = "C:\PHP\ext"


extension=php_pdo.dll


Importing the Rules


Next thing you need is to import the rules from the .htaccess file.


Open the Internet Information Services (IIS) Manager on the left pane click on your site (for details on how to configure IIS for Symfony take a look at [2]) and you´ll see an icon URL Rewrite


201107231152.jpg


Double click on the URL Rewrite icon. On the left pane named Actions click on Import Rules…


Select the .htaccess file on your web folder and click Import. Click on Apply.


201107231153.jpg

Click on Back to Rules… and you´ll see the rules imported.


201107231153.jpg

What this does is write a web.config file on your web folder than contains the rules for the URL Rewrite module and other fun thing Microsoft deems important.


Note: If you´re using rsync to update your site with –delete option be sure to copy the web.config file otherwise it will delete every time you update with rsync.


rsync -avr --delete --exclude-from=‘ path/exclude.txt‘ source/dir target/dir


201107231154.jpg


CREATOR OWNER SPECIAL


SYSTEM FULL CONTROL


ADMGISSVR SPECIAL


Administrators Full Control


Users Read & execute, List folder contents, Read


References


[1] http://www.iis.net/download/urlrewrite


[2] http://www.symfony-project.org/more-with-symfony/1_4/en/11-Windows-and-Symfony



jueves, 10 de marzo de 2011

Making IIS 7 serve MrSID Files

Until IIS 6 in order to serve MrSID files all you had to do was assign the proper NTFS permissions to the folder where the files were located and create a virtual directory in IIS. If you only do that and try to access a URL to an MrSID file on II 7 you´ll get a “404 - File or directory not found.” . The error message is confusing since the directory does exist. Finally figured there where restrictions with the MIME type, it seems IIS 7 only serves MIME you specifically declared.

To declare a new MIME type for MrSID you have to add an additional step.

1. Open the IIS Manager

2. Click on the virtual directory or directory

3. Click on MIME Types

4. Click on Add…

5. Fill the File name extension as .sid and the MIME type as application/octet-stream


6. Click Ok.

Now you should be able to access your MrSID via an URL.

martes, 8 de marzo de 2011

Using Pagination with Symfony and Doctrine 2

When I blogged Using Pagination with Symfony and Doctrine I had being trying to understand the inner working of the Symfony's Admin Generator. At the time it seem a good solution. But, when I started implementing the idea, I realized that I had to repeat very similar code on each of the of the actions of the modules I was working.
So I decided to try a different aproach. I put all de code I was using in the actions in a utility class, created a widget to render the header and added a partial to render how many items in the table and the total number of pages. The final goal was to create my first Symfony plugin, but I'm not quite there yet, so I decided to blog first this improvement.
The Pagination Control
Since it's the same control I proposed in my previous blog Using Pagination with Symfony and Doctrine I will not repeat the code. You still need to get the images for buttons and place them in your web\images folder.
The Pagination Info Control
Create a file named /myproject/apps/myapplication/templates/_pagination_info.php and copy the following code on it. Then save it.
<div class="pagination_info">
<?php echo format_number_choice('[0] no result|[1] 1 result|(1,+Inf] %1% results',
array('%1%' => $pager->getNbResults()), $pager->getNbResults(), 'sf_admin') ?>
<?php if ($pager->haveToPaginate()): ?>
<?php echo __('(page %%page%%/%%nb_pages%%)', array('%%page%%' => $pager->getPage(),
'%%nb_pages%%' => $pager->getLastPage()), 'sf_admin') ?>
<?php endif; ?>
</div>
Creating the Paging Utility
Create a file named /myproject/lib/model/PagingUtility.class.php and copy the following code on it. Then save it.
This class will take care of all the code previously had to copy into the actions.
class PagingUtility {
private $modelname;
private $sortkey;
private $user;
private $modulename;

public function __construct($modelname, $user) {
$this->modelname=$modelname;
$this->user =$user;
$this->sortkey =strtolower($this->modelname).".sort";
$this->modulename = strtolower($this->modelname) . "_module";
}

public function buildPager($request, $items_x_page,$query=NULL){
if ($request->getParameter('sort') && $this->isValidSortColumn(
$request->getParameter('sort'))) {
$this->setSort(array($request->getParameter('sort'),
$request->getParameter('sort_type')));
}

// $items_x_page = sfConfig::get('app_max_items_x_page');
$pager = new sfDoctrinePager($this->modelname, $items_x_page);
//$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
$query = is_null($query) ? $this->buildQuery() : $query;
$pager->setQuery($query);
$pager->setPage($request->getParameter('page', 1));
$pager->init();
return $pager;
// $this->sort = $this->getSort();
}
protected function buildQuery() {
$et = Doctrine_Core::getTable($this->modelname);

$query = $et->createQuery();

$this->addSortQuery($query);

return $query;
}

protected function addSortQuery($query) {
if (array(null, null) == ($sort = $this->getSort())) {
return;
}

if (!in_array(strtolower($sort[1]), array('asc', 'desc'))) {
$sort[1] = 'asc';
}
$query->addOrderBy($sort[0] . ' ' . $sort[1]);
}

public function getSort() {
if (null !== $sort = $this->user->getAttribute($this->sortkey,
null, $this->modulename)) {
return $sort;
}

$this->setSort(array(null, null));

return $this->user->getAttribute($this->sortkey,
null, $this->modulename);
}

protected function setSort(array $sort) {
if (null !== $sort[0] && null === $sort[1]) {
$sort[1] = 'asc';
}
$this->user->setAttribute($this->sortkey, $sort, $this->modulename);
}

protected function isValidSortColumn($column) {
return Doctrine::getTable($this->modelname)->hasColumn($column);
}
}
Creating the Sortable Header Widget
I'm not sure if creating a widget for this purpose would be the best approach, but I wanted to make my own widget a see how it worked. The code is an adaption of the code from a previous blog entry Sorting Lists in Symfony.
Create a file named /myproject/lib/widget/maSortableHeader.php and copy the following code on it. Then save it.
class maSortableHeader extends sfWidgetForm {

protected function configure($options = array(), $attributes = array()) {
$this->addOption('fieldname');
$this->addOption('fieldlabel');
$this->addOption('routename');
$this->addOption('sort');
}

public function render($name, $value = null, $attributes = array(), $errors = array()) {
$sort = $this->getOption('sort');

if ($this->getOption('fieldname') == $sort[0]) {
$config = array('query_string' => 'sort=' . $this->getOption('fieldname') . '&sort_type=' .
($sort[1] == 'asc' ? 'desc' : 'asc'));
$uri = link_to($this->getOption('fieldlabel'), $this->getOption('routename'),
$config);
$uri .= image_tag('/images/' .
$sort[1] . '.png', array('alt' => $sort[1],
'title' => $sort[1]));
} else {
$uri = link_to($this->getOption('fieldlabel'), $this->getOption('routename'),
array('query_string' => 'sort=' . $this->getOption('fieldname') . '&sort_type=asc'));
}

return $uri;
//optional - for easy css styling
}

}
Editing the Action


On our action we're going to implement our PagingUtiliy and add widets for two columns (Code and Name). Our executeIndex function looks like this:
public function executeIndex(sfWebRequest $request)
{
$pu = new PagingUtility("Category", $this->getUser());
$items_x_page = sfConfig::get('app_max_items_x_page');
$this->pager = $pu->buildPager($request, $items_x_page);

$this->sort = $pu->getSort();

//Adding SortableHeaders
$this->form = new sfForm();
$this->form->setWidget('code_sorted',
new maSortableHeader(array('fieldname'=>'code',
'fieldlabel'=>'Code', 'routename'=>'@category','sort'=>$pu->getSort())));
$this->form->setWidget('name_sorted',
new maSortableHeader(array('fieldname'=>'Name',
'fieldlabel'=>'Name', 'routename'=>'@category','sort'=>$pu->getSort())));

}
You still need to get the asc.png and desc.png into your web/images folder for the widget to show icon besides the header.

Editing the indexSuccess.php
What we need to do now is show the Sortable Headers, change the default loop to use the pager, add the pagination control and the pagination info partials.
<thead>
<tr>
<th><?php echo $form['code_sorted']; ?></th>
<th><?php echo $form['name_sorted']; ?></th>
<th>Description</th>
<th>Created at</th>
<th>Updated at</th>
<th>Created by</th>
<th>Updated by</th>
</tr>
</thead>
<tbody>
<?php foreach ($pager->getResults() as $i => $category): ?>
<tr>
<td><a href="<?php echo url_for('category/edit?id='.$category->getId()) ?>"><?php echo $category->getCode() ?></a></td>
<td><?php echo $category->getName() ?></td>
<td><?php echo $category->getDescription() ?></td>
<td><?php echo $category->getCreatedAt() ?></td>
<td><?php echo $category->getUpdatedAt() ?></td>
<td><?php echo $category->getCreator() ?></td>
<td><?php echo $category->getUpdator() ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<td colspan="7">
<?php include_partial('global/pagination_info', array('pager' => $pager)) ?>
<?php include_partial('global/pagination_control', array('pager' => $pager, 'module' => 'category')) ?>
</td>
</tr>

Editing the Routing.yml
This is the step I always forget. Mainly because I don't quite undertand what it does. I will look into it an post my findings. For now it will not work unless you add the follwing code to your routing.yml.
category:
class: sfDoctrineRouteCollection
options:
model: Category
module: category
prefix_path: /category
column: id
with_wildcard_routes: true

jueves, 3 de marzo de 2011

QR Codes a Cool Way to Share Data

I recently discovered QR Codes. QR Codes (QR is short for Quick Read) were created by Denso-Wave a subsidiary of Toyota. They are a type of barcode that can be read by both barcode readers and smartphones with cameras. This is my blogs url encoded in QR Code:

These codes are being used extensively in advertising and even in games. But the thing I like the most is there are several QR code generator online.

The jewel of the crown is that a QR Code are set down in the ISO-18004 standard so they are the same all over the world.

QR Code Online Generators

There are several online QR Code Generators. The followings are the ones I took a peek at:

  1. I found Kaywa QR Code to be very good.

  2. QR Code Generator from the ZXing Project: Is based on the Googles Chart API and has more types of contents for example has a WIFI Network format. It also has a geo location format which is great.

    This the URL for the Biodiversity Museum in Panama.
  3. QRStuff : has more data types than XZing Project but my QR reader doesn't work with all of them. I create a QR a code for an email message and even though the reader read it, I got an error when I tried to send the email. I like this QR generator site because it has a lot of information on QR codes.
Now if you're sort of a nerd (like me) you are probable thinking how to embed the online creators into your own application. The coolest thing about QR codes it's that they are supported the Google Chart API. For example to create a QR code to encode the phrase Hola Panama (Hello Panama) all you have to do is build an URL like this one:

http://chart.apis.google.com/chart?cht=qr&chs=350x350&chl=Hola+Panama

You'll get a QR code like this


QR Code Reader

Since I have Blackberry I just focused on readers for BBs. But you can find readers for iPhone, Android and certain versions of Nokias OSs. I found a list of barcode readers can be found at http://www.mobile-barcodes.com/qr-code-software/blackberry/.
I chose i-nigma only because it supported many other phones aside from Blackberry. Once installed it has been working great.