Thursday, May 21, 2015

Setup WAMP Environment and Create a Symfony Project

To setup a WAMP environment (a web server of Apache, PHP, MySQL in Windows platform), you can install the packages one by one manually, or choose a much easier way: install WampServer.

WampServer is a Windows web development environment. It allows you to create web applications with Apache2, PHP and a MySQL database. Alongside, PhpMyAdmin allows you to manage easily your databases.

WampServer installs automatically all you need to start developing web applications and is very intuitive to use. You will be able to tune your server without even touching the setting files.

Go to the WampServer site to download: WampServer Site

Note:
You might encounter with error "Missing MSVCR100.DLL or MSVCR110.DLL" during the installation of WampServer. It means you need to install Microsoft C/C++ Redistributable runtime libraries. It is available from Microsoft website. The detailed solution is provided here: Missing MSVCRxxx.DLL Thread.

Restart computer after intallation completed. Now I have Apache, PHP, MySQL installed.
Start WampServer: Start --> All Apps --> WampServer --> start WampServer

Try the Web server at a browser with "localhost", or find and click on the WampServer icon in task bar at right bottom corner --> Localhost. Tada! Your WAMP web server is running!

Now I need to create a symfony project.

Go to your project folder, i.e.
cd C:\wamp\www

Note:
To make less typing and make it easier to run php in command line, you can add the location of php.exe to environment variable PATH. (Control Panel-->System and Security-->System-->Change settings-->Advanced-->Environment Variables-->System variables>Path-->Edit, Add ';C:\wamp\bin\php\php5.5.12' at the end of value. Restart Command Prompt to let the new path take effect.

Install symfony:
 ..\bin\php\php5.5.12\php.exe -r "readfile('http://symfony.com/installer');" > symfony 

Create the project:
 ..\bin\php\php5.5.12\php.exe new myProject
Wait a while for Symfony to download the packages and prepare the project. When it's done,
cd myProject
 ..\..\bin\php\php5.5.12\php.exe app\console server:run

Now test the symfony project at browser:
localhost:8000





Monday, May 04, 2015

[Symfony] Database and Doctrine (2) - Entity Relationships

  
Entity Relationships/Associations



Creating the Category entity. Let Doctrine create the class for you.

$ php app/console doctrine:generate:entity \
   --entity="AppBundle:Category" \
   --fields="name:string(255)"

Relationship Mapping Metadata

To relate the Category and Product entities, start by creating a products property on the Category class.

# src/AppBundle/Resources/config/doctrine/Category.orm.yml
AppBundle\Entity\Category:
   type: entity
   # ...
   oneToMany:
       products:
           targetEntity: Product
           mappedBy: category
   # don't forget to init the collection in the Category::__construct() method
   # $this->products = new ArrayCollection();
   # of the entity
First, since a Category object will relate to many Product objects, a products array property is added to hold those Product objects. This isn't done because Doctrine needs it, but because it makes sense in the application for each Category to hold an array of Product objects.
// src/AppBundle/Entity/Category.php

use Doctrine\Common\Collections\ArrayCollection;

class Category
{
    // ...

    /**
    * @ORM\OneToMany(targetEntity="Product", mappedBy="category")
    */
    protected $products;

    public function __construct()
    {
       $this->products = new ArrayCollection();
    }
}

Next, since each Product class can relate to exactly one Category object, you'll want to add a $category property to the Product class:
// src/AppBundle/Entity/Product.php
class Product
{
    // ...

    /**
    * @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
    * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
    */
    protected $category;
}

Finally, tell Doctrine to generate the missing getter and setter methods of the added new property in both the Category and Product classes:
$ php app/console doctrine:generate:entities AppBundle


Before you continue, be sure to tell Doctrine to add the new category table, and product.category_id column, and new foreign key:
$ php app/console doctrine:schema:update --force



Saving Related Entities

use AppBundle\Entity\Category;
use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
   public function createProductAction()
   {
       $category = new Category();
      //set category data
       
  $product = new Product();
       // Set product data   
  // relate this product to the category
       $product->setCategory($category);

       $em = $this->getDoctrine()->getManager();
       $em->persist($category);
       $em->persist($product);
       $em->flush();

       return new Response(
           'Created product id: '.$product->getId()
           .' and category id: '.$category->getId()
       );
   }
}
Now, a single row is added to both the category and product tables. The product.category_id column for the new product is set to whatever the id is of the new category. Doctrine manages the persistence of this relationship for you.

Fetching Related Objects

public function showAction($id)
{
    $product = $this->getDoctrine()
       ->getRepository('AppBundle:Product')
       ->find($id);

    $categoryName = $product->getCategory()->getName();
    // ...
}

When you call $product->getCategory()->getName(), Doctrine silently makes a second query to find the Category that's related to this Product. It prepares the $category object and returns it to you.
You can also query in the other direction:
public function showProductsAction($id)
{
   $category = $this->getDoctrine()
       ->getRepository('AppBundle:Category')
       ->find($id);

   $products = $category->getProducts();
   // ...
}

Joining Related Records

You can avoid the second query by issuing a join in the original query. Add the following method to the ProductRepository class:

// src/AppBundle/Entity/ProductRepository.php
public function findOneByIdJoinedToCategory($id)
{
   $query = $this->getEntityManager()
       ->createQuery(
           'SELECT p, c FROM AppBundle:Product p
           JOIN p.category c
           WHERE p.id = :id'
       )->setParameter('id', $id);

   try {
       return $query->getSingleResult();
   } catch (\Doctrine\ORM\NoResultException $e) {
       return null;
   }
}

Lifecycle Callbacks

Sometimes, you need to perform an action right before or after an entity is inserted, updated, or deleted. These types of actions are known as "lifecycle" callbacks.

# src/AppBundle/Resources/config/doctrine/Product.orm.yml
AppBundle\Entity\Product:
    type: entity
    # ...
    lifecycleCallbacks:
       prePersist: [setCreatedAtValue]

====================================
// src/AppBundle/Entity/Product.php
/**
* @ORM\PrePersist
*/
public function setCreatedAtValue()
{
    $this->createdAt = new \DateTime();
} 

Reference

Symfony Doctrine

[Symfony] Database and Doctrine (1) - DB, Entity, Repository

First, we can link up the database by configuring app/config/parameters.yml: change the database name, user and password to reflect your own choices.

If your database doesn't exists:

 Create database:

$php app/console doctrine:database:create
We are going to create an Entity called Page with text and body:
php app/console doctrine:generate:entity --entity=AppBundle:Page \
 --format=annotation --fields="title:string(255) body:text" \
 --no-interaction
php app/console doctrine:database:create
php app/console doctrine:schema:create

Doctrine can automatically create all the database tables needed for every known entity in your application. To do this, run:
$php app/console doctrine:schema:update --force

If you already have database, import it:
- import database ( mysql <import.sql )
- import these database structures into Symfony:
$php app/console doctrine:mapping:import AppBundle yml

The database mapping is created in src/AppBundle/Resources/config/doctrine/

Now we need the form for that entity, another generator command :
php app/console doctrine:generate:form AppBundle:Page --no-interaction

Doctrine Mapping Types

  • string: Type that maps a SQL VARCHAR to a PHP string.
  • integer: Type that maps a SQL INT to a PHP integer.
  • smallint: Type that maps a database SMALLINT to a PHP integer.
  • bigint: Type that maps a database BIGINT to a PHP string.
  • boolean: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
  • decimal: Type that maps a SQL DECIMAL to a PHP string.
  • date: Type that maps a SQL DATETIME to a PHP DateTime object.
  • time: Type that maps a SQL TIME to a PHP DateTime object.
  • datetime: Type that maps a SQL DATETIME/TIMESTAMP to a PHP DateTime object.
  • datetimetz: Type that maps a SQL DATETIME/TIMESTAMP to a PHP DateTime object with timezone.
  • text: Type that maps a SQL CLOB to a PHP string.
  • object: Type that maps a SQL CLOB to a PHP object using serialize() and unserialize()
  • array: Type that maps a SQL CLOB to a PHP array using serialize() and unserialize()
  • simple_array: Type that maps a SQL CLOB to a PHP array using implode() and explode(), with a comma as delimiter. IMPORTANT Only use this type if you are sure that your values cannot contain a ”,”.
  • json_array: Type that maps a SQL CLOB to a PHP array using json_encode() and json_decode()
  • float: Type that maps a SQL Float (Double Precision) to a PHP double. IMPORTANT: Works only with locale settings that use decimal points as separator.
  • guid: Type that maps a database GUID/UUID to a PHP string. Defaults to varchar but uses a specific type if the platform supports it.
  • blob: Type that maps a SQL BLOB to a PHP resource stream

Generating Getters and Setters

# generates all entities of bundles in the AppBundle namespace
$ php app/console doctrine:generate:entities --path="src/" AppBundle

$ php app/console doctrine:generate:entities AppBundle/Entity/Product

Entity files are created in src/AppBundle/Entity/.

Persisting an object to DB:
    $em = $this->getDoctrine()->getManager();
    $em->persist($product);
    $em->flush();

Fetching object from DB:
    $product = $this->getDoctrine()
       ->getRepository('AppBundle:Product')
       ->find($id);

// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');

// find *all* products
$products = $repository->findAll();

// find a group of products based on an arbitrary column value
$products = $repository->findByPrice(19.99);

// query for one product matching by name and price
$product = $repository->findOneBy(
    array('name' => 'foo', 'price' => 19.99)
);

// query for all products matching the name, ordered by price
$products = $repository->findBy(
    array('name' => 'foo'),
    array('price' => 'ASC')
);

    if (!$product) {
       throw $this->createNotFoundException(
           'No product found for id '.$id
       );
    }
Updating an object
    $em = $this->getDoctrine()->getManager();
    $product = $em->getRepository('AppBundle:Product')->find($id);

    if (!$product) {
       throw $this->createNotFoundException(
           'No product found for id '.$id
       );
    }

    $product->setName('New product name!');
    $em->flush();
Delete an object:
$em->remove($product);
$em->flush();
Doctrine’s Query Builder:
$repository = $this->getDoctrine()
    ->getRepository('AppBundle:Product');

$query = $repository->createQueryBuilder('p')
    ->where('p.price > :price')
    ->setParameter('price', '19.99')
    ->orderBy('p.price', 'ASC')
    ->getQuery();

$products = $query->getResult();
$product = $query->getSingleResult();
$product = $query->getOneOrNullResult();
DQL:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
    'SELECT p
    FROM AppBundle:Product p
    WHERE p.price > :price
    ORDER BY p.price ASC'
)->setParameter('price', '19.99');

$products = $query->getResult();

Custom Repository Classes

To create a repository to hold all the customized database manipulation methods, you need to:
  • Add the name of the repository class to your mapping definition. (located at src/AppBundle/Resources/config/doctrine/*.orm.yml)

    For AppBundle\Entity\Product:
    type: entity
    repositoryClass: AppBundle\Entity\ProductRepository
    table: product
    fields:
          id:...

Doctrine can generate the repository class for you by running the same command used earlier to generate the missing getter and setter methods:
php app/console doctrine:generate:entities --path="src/" AppBundle

You will notice a new ProductRepository.php file is created under src/AppBundle/Entity.
  • Create all necessary functions to provide required database manipulation capabilities (like retrieving data).
  •  
     

Reference

Symfony Doctrine