Páginas

17 de outubro de 2011

We have a new home!

Dear Dicas-Symfony readers (if any),




We have a new blog over at
http://it.blog.adclick.pt


All posts from this blog were transferred.

Hope to see you there!

29 de agosto de 2011

Propel get average - mysql AVG

Here's how to do it:

public function getAverageRating()
  {
    $c = new Criteria;
    $c->addSelectColumn('avg(' . RatingPeer::RATING . ')');
    $c->add(RatingPeer::ID, $this->getId());
    
    $stmt = RatingPeer::doSelectStmt($c);    
    $average = $stmt->fetchAll(PDO::FETCH_COLUMN);
    if(array_key_exists(0, $average)) {
      return floatval($average[0]);
    }
    else
    {
      return 0;
    }
  }

26 de agosto de 2011

Using CKEditor and CKFinder


+

  1. Download the sfCKEditorPlugin plugin
  2. Go to http://ckeditor.com/ and download the last version.
  3. Put the ckeditor folder to the web\js folder.
  4. Go to http://ckfinder.com/ and download the last version.
  5. Put the ckfinder folder to the web\js folder.
  6. Add sfCKEditorPlugin to the ProjectConfiguration.class.php
  7. Open app.yml (apps/frontend or backend/config..) and put:
      # sfCKEditorPlugin
      ckeditor:
        basePath: /js/ckeditor
      ckfinder:
        active: true
        basePath: /js/ckfinder
  8. Create config/autoload.yml:
    autoload:
      ckeditor:
        name:       ckeditor
        path:       %sf_web_dir%/js/ckeditor
        recursive:  on
        
      ckfinder:
        name:       ckfinder
        path:       %sf_web_dir%/js/ckfinder
        recursive:  on
  9. Add js files to the view.yml in this way:
    javascripts: [ckeditor/ckeditor.js, ckfinder/ckfinder.js]
  10. Edit /js/ckfinder/config.php... find CheckAuthentication() method and modify it toreturn true; however read the comments, it can be dangerous!
  11. Change the $baseUrl to /uploads/ckfinder/ in ckfinder's config.php
  12. Type symfony cc to clear the cache.
  13. Use the following syntax to call CKEditor:
    $this->widgetSchema['my_editor'] = new sfWidgetFormCKEditor();


From: http://sakrawebstudio.blogspot.com/2010/10/install-ckeditor-and-ckfinder-in.html

1 de agosto de 2011

Where did a certain query originated?

Easy way of tracking every sql query under sf's the web debug toolbar:

#factories.yml:
dev:
  logger:
    param:
      loggers:
        sf_web_debug:
          param:
            xdebug_logging: true

Result:

21 de julho de 2011

Symfony a nossa missão

Qualquer departamento de IT, para ter sucesso, necessita de saber para que é que existe! À medida que um departamento cresce, essa necessidade é fundamental.
Também nós, GEEKS, chegamos a um consenso sobre a nossa MISSÃO.

Symfony Nosso que estais no svn,
santificado seja o vosso php,
vem a nós o vosso MVC,
seja feita a vossa aplicação
assim na terra como na web.

A cache nossa de cada dia nos daí hoje,
perdoai-nos os nossos bugs,
assim como nós perdoamos
ao Ti Nano,
não nos deixei cair em tentação
mas livrai-nos do Ruby.

Amém.
Décio @ Yammer

19 de julho de 2011

Doctrine Slugify / Urlize

When you need to create an url use that simple way:

$category = 'Pastéis de Nata';
$url = Doctrine_Inflector::urlize($category);
echo $url;

return:
pasteis-de-nata

___

15 de julho de 2011

Distinct backend Filters for users

Simple way of showing different backend filters for different users:

lib/filter/doctrine/YourFormFilter.class.php
class YourFormFilter extends BaseYourFormFilter {
  public function setup() {
    // ...

    $context = sfContext::getInstance(); /* @var $context sfContext */
    $request = $context->getRequest(); /* @var $request sfWebRequest */
    $user = $context->getUser(); /* @var $user sfBasicSecurityUser */
        
    if ($user->hasCredential('credential_to_check')) {
      $this->setWidget('your_field', new sfWidgetFormFilterInput());
      $this->setValidator('your_field', new sfValidatorPass(array('required' => false)));
      $this->widgetSchema->moveField('your_field', sfWidgetFormSchema::AFTER, 'other_field'); // this is good to re-arrange the filters order
    }
  }

  // ...

15 de junho de 2011

Criar sfValidor

Quando for necessário criar uma verificação especifica para um dos campos do formulário basta-nos personalizar a validação do(s) campo(s) da seguinte forma:

FormularioForm.class.php


public function configure() {
    parent::configure();

    $this->validatorSchema['my_field'] = new sfValidatorCallback(
                        array('callback' =>
                                array($this, 'validateMyField')
                        )
    );
}

public function validateMyField($validator, $value) {
    /* All my action goes here */

    if($is_everything_ok){
        return $value;
    }else{
        return new sfValidatorError($validator, "I guess something goes wrong here!");
    }
}

Et voilá...

14 de junho de 2011

Propel, extender schema de plugins

Quando precisamos de adicionar campos novos a uma tabela gerada por um plugin temos que criar um myPlugin_schema.custom.yml onde myPlugin é o nome do plugin.

Caso prático: Adicionar o campo 'coisas' na tabela sf_guard_user (gerada pelo plugin sfGuardPlugin)

1 - criar o ficheiro sfGuardPlugin_schema.custom.yml em project/config/
propel:
  _attributes:      { package: plugins.sfGuardPlugin.lib.model }
  
  ##
  # extending sfGuard 
  ##
  sf_guard_user:
    _attributes:    { phpName: sfGuardUser }
    coisas:         { type: varchar, size: 128, required: true }

2 - fazer re-build das classes
php symfony propel:build --all-classes

Done!

13 de junho de 2011

Servir documentos através de uma action

Por vezes, torna-se necessários servir ficheiros privados, que em vez de estarem no diretório público web/uploads, devem ficar fora de web, impedindo o acesso direto, cabendo a uma action verificar se o utilizador tem permissões e então entregar o ficheiro. Eis como o fazer através do object sfResponse numa action:

public function executeBackup(sfWebRequest $request)
{
// realizar backup da base de dados e dos uploads
$this->setLayout(false);
$this->setTemplate(false);
sfConfig::set('sf_web_debug', false);

// nome do ficheiro tem de coincidir com o definido na task uacBackupTask
$file_cache = sfConfig::get('sf_data_dir').'/backup/backup_'.date('Y-m-d').'.zip';
// verificar se existe ficheiro de backup criado pela task, se sim, entrega esse, senão gera novo
if (file_exists($file_cache))
{
$file = $file_cache;
}
else
{
$file = adcBackup::execute(); // cria o ficheiro e envia o caminho absoluto
$backup = sfConfig::get('sf_data_dir').'/backup/'.basename($file);
@copy($file, $backup);
@chmod($backup, 0666);
}

// check if the file exists
$this->forward404Unless(file_exists($file));

// Adding the file to the Response object
$this->getResponse()->clearHttpHeaders();
$this->getResponse()->addCacheControlHttpHeader('Cache-control','must-revalidate, post-check=0, pre-check=0');
$this->getResponse()->setHttpHeader('Pragma: public', true);
$this->getResponse()->setContentType('application/zip');
$this->getResponse()->setHttpHeader('Content-Disposition','attachment; filename='.basename($file), true);
$this->getResponse()->sendHttpHeaders();
$this->getResponse()->setContent(file_get_contents($file));

return sfView::NONE;
}

8 de junho de 2011

symfony & Doctrine behavior review

The Doctrine ORM is bundled with several behaviors (calles core behaviors), these are:


  • Versionable - add an entire XxxVersion table to store your Xxx model object versions as either only version numbers or numbers versions along with ALL column data - to see object data changes through time,

  • Timestampable - probably the most popular of all behaviors, adds created_at and updated_at timestamp columns to your model, automatically saving datetime when a record is created or updated, respecticely,


  • Sluggable - adds a slug column that stores a slug which is a unique index automatically and can be used along with sfDoctrineRoute class to refer to an object without passing its ID as a parameter; by default the update is disabled, since this may harm your search engines rating,

  • I18n - add an entire XxxTranslation table to provide Internationalization (I18n) for your Xxx model, essential when developing a multi-language project,


  • NestedSet - adds root_id, lft, rgt and level columns to your model to develop an advanced hierarchical data structure (such as product categories), nested sets is an alternative to adjacency model, more details on hierarchical data here,

  • Searchable - choose model columns you want to index and add an entire database table, speeding up a basic search engine development, more info about searching here,


  • Geographical - adds longitude and latitude columns storing geographical coordinates. Ever needed to use gmaps in your project, along with sfEasyGMapPlugin? Not only this behavior suits the data structure you need, but also provides you with getDistance() method to calculate distance between two Geographical objects,


  • SoftDelete - adds a deleted_at column which defines if a record has been marked as deleted (and if so, when). Useful when designing a highly complicated system where data consistency is important and even if some data should be invisible in the backend, it should still remain in the database.

extension behaviors

You may also use Doctrine extensions:

  • Blameable - adds an additional level of auditing capabilities to models, blameable allows you to track who created or last updated any model in an environment with many users, blameable is a great companion to Timestampable behavior,


  • EventLoggable, readme - logs any Doctrine Events (pre/post Delete/Insert/...) fired upon a record, log type may be chosen (e.g. file) and events may be limited to the chosen ones only,

  • Localizable - gives you functionality to convert units of measurement (e.g. kilometers to miles, etc.) or any other conversions desirable,

  • Locatable - gives you functionality using the Google Maps API to automatically populate your model with latitude and longitide information using Google, a fantastic tool to use along with sfEasyGMapPlugin,

  • Sortable - gives you sortable functionality to your models, enabling you to easily fetch next/previous objects, moving them up/down in a sorted list or swapping with another object,


  • Taggable - adds Tagging capabilities, creates a TaggableTag table and a XxxTaggableTag table for each taggable model, using refClass m:n db table relation, provides easy tag management: add, remove, remove all, set, etc, finally, gives you the possibility to get the most popular tags (for a tag cloud, for example) fetched either from chosen models or from all of them,

plugin behaviors

Finally, there are also symfony plugins providing custom behaviors:



useful links about Doctrine behaviors:


http://symfony-world.blogspot.com/2010/10/symfonydoctrine-behavior-review.html

16 de maio de 2011

Ignore cache on a specific component

If you globally activated cache on your application but need a specific component to skip cache:

On app_name/modules/module_name/config/cache.yml:
_componentName:
  enabled: false

Note: this will deactivate cache on _componentName.php

24 de abril de 2011

Integração dos erros com o netbeans

Em ambiente dev, quando ocorre um erro, o symfony detalha todo o percurso realizado até à obtenção do mesmo, indicado os ficheiros e linhas executadas. É possível tornar os ficheiros clicáveis e abrirem diretamente no netbeans.

1) configurar o firefox para entender o protocolo netbeans:
  • digitar na barra de endereços about:config
  • aceitar o aviso de alerta e continuar
  • clicar com o lado direito e criar um novo valor do tipo boleano
  • colocar o nome network.protocol-handler.expose.netbeans
  • atribuir o valor false
  • reiniciar o firefox
2) criar script bash que vai interpretar o endereço do ficheiro e mapear para o netbeans, por exemplo, em ~/bin/file_link_format.sh

#!/bin/bash
url=$1
file=${url#*\/\/}
file=${file%?line=*}
line=${url#*line=}
if [ "x$line" != "x" ]; then
line=:$line
fi
/usr/local/netbeans-6.9/bin/netbeans --jdkhome /usr/java/default/ --open $file$line

Acertar na última linha o caminho para o executável do netbeans e a localização do java.

3) Configurar o projeto symfony para usar um apontar para os ficheiros, em settings.yml, no ambiente dev:
dev:
.settings:
file_link_format: netbeans://%f?line=%l

No ambiente de desenvolvimento, na barra de informação, no painel view, clicar num ficheiro, surgirá uma janela do firefox alertando que desconhece o protocolo netbeans, aponta para o executável criado em 2) e já está, abre o netbeans no ficheiro e linha específicos.

Informação testada em netbeans 6.9 e firefox 3.6.16, com o symfony 1.4. Baseado no artigo http://www.symfony.it/articoli/223/link-diretti-a-neatbeans-con-file-link-format/

18 de abril de 2011

Uma alternativa para a administração

Um plugin para modificar e ultrapassar algumas limitações do generate-admin nativo do symfony: http://www.symfony-project.org/plugins/sfHadoriThemePlugin

Pode ser testado em http://brentertainment.com/2011/04/13/hadori-symfonys-brand-new-admin-generator/

Acho que ainda precisa de algumas afinações de layout, mas não deixa de ser interessa a implementação dos filtros e a exportação em csv.

5 de abril de 2011

Hint to avoid memory exhausted error (symfony + doctrine1.2)

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 71 bytes) in /blablabla/lib/vendor/symfony/lib/log/sfVarLogger.class.php on line 170


The problem is that the doctrine profiler is enabled for sfBaseTask, as sf_debug is set to true.

To disable the profiler, you have to create a new environment in config/databases.yml:
task:
  symfony:
      param:
        profiler: false

all:
  symfony:
    class: sfDoctrineDatabase
      param:
        dsn:   ....
        username:   ...
        password:   ...


From: http://www.nacho-martin.com/hint-to-avoid-memory-exhausted-error-symfony-doctrine1-2

23 de março de 2011

Verificar memória ocupada numa task symfony

Quem usa objectos em PHP já se deve ter deparado com esta frase:

As of version 5.2.5, PHP is not able to garbage collect object graphs that have circular references, e.g. Parent has a reference to Child which has a reference to Parent. Since many doctrine model objects have such relations, PHP will not free their memory even when the objects go out of scope.

Ou seja, para objectos mais "complexos" nem o unset() resulta pois o PHP não consegue libertar os objectos ligados, que é o caso dos objectos model.
Nesse caso o symfony tenta dar uma ajuda com o método $meu_objecto->free().
Ou seja, para limpar:
$meu_objecto->free();
unset($meu_objecto);

Mesmo assim já tive casos em que isso não resolveu completamente e a solução foi não usar o Doctrine, outras vezes executar o ciclo um número limitado de vezes dentro do mesmo processo (task).
Para calcular o número de ciclos que o processo aguenta antes de recebntar com a memória usei este pedaço de código dentro do ciclo (de uma task) para ir vendo como evolui o consumo de memória.

//memory check
$memOld = $memNow;
$memNow = memory_get_usage();
$this->log('Memory usage: ' . $memNow . ' (diff=' . ($memNow - $memOld) . ')';

14 de fevereiro de 2011

Criar Doctrine_Collection manualmente

Útil quando não temos dados suficientes em base de dados para fazer alguns testes:
/* @var $dc Doctrine_Collection */
$dc = new Doctrine_Collection('City');
for ($i=1;$i<=32;$i++)
{
   $record = new City();
   $record->setName('City #' .$i);
   $dc->add($record);
}
$this->cities = $dc;

i18n nas actions

Como sabem os helpers estão apenas disponíveis na View, como tal chamar a função __("texto") numa action não funciona.

Como alternativa, numa action.class.php, podemos utilizar a seguinte técnica:
$this->getContext()->getI18N()->__("texto");

Outra forma é forçar o load dos helpers necessários na configuração da aplicação - isto faz com que a função __("texto") fique disponível em toda a aplicação:
class frontendConfiguration extends sfApplicationConfiguration {

    public function configure()
    {
        $this->loadHelpers(array('I18N'));
    }

}

11 de fevereiro de 2011

Detectar primeira visita de utilizador

Para detectarmos o primeiro request (visita) do utilizador (logado ou não), basta adicionar o seguinte método à class do user (app_name/lib/myUser.class.php):

public function isFirstRequest()
    {
        $first_request_parameter = $this->getAttribute('first_request');
        if (isset($first_request_parameter)) {
            return false;
        }

        $this->setAttribute('first_request', true);
        return true;
    }

Agora numa action:

...
if ($this->getUser()->isFirstRequest()) {
  // primeira visita
} else {
  // re-visita
}

1 de fevereiro de 2011

Usar Helpers nas Tasks

Para utilizar helpers nas tasks, precisamos de referenciar qual a aplicação da task. Para isso forçamos uma aplicação default nas options da task pretendida:

protected function configure()
{
        $this->addOptions(array(
            new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED,'The application name', 'frontend'),
                // ...
        ));
}
(Neste caso defini a aplicação default como sendo frontend)

Agora, no método execute:

protected function execute($arguments = array(), $options = array())
{
        // ...
        $contextInstance = sfContext::createInstance($this->configuration);
        $contextInstance->getConfiguration()->loadHelpers('Partial');
        // ...
}

31 de janeiro de 2011

Esconder Web Debug toolbar

Para desligar a web debug toolbar numa determinada action basta:

public function executeShow(sfWebRequest $request)
    {
        sfConfig::set('sf_web_debug', false);
    }

Isto é particularmente útil quando queremos abrir modalbox's cuja formatação fica estragada quando a dita toolbar está presente.

29 de janeiro de 2011

Form Validator usando dois campos

Os Validators dos forms normalmente apenas se aplicam a um campo, mas há formas de comparar vários campos e fazer uma validação conjunta.
Neste exemplo nem sequer foi preciso criar um Custom Validator, porque a simples comparação já é possível:
class TransactionForm extends BaseTransactionForm {

    public function configure() {
  
        $this->mergePostValidator(
new sfValidatorSchemaCompare('source_id', 
                             sfValidatorSchemaCompare::NOT_EQUAL, 
                             'destination_id', 
                             array(), 
                             array('invalid' => 'Source and Destination Accounts cannot be the same.')));

    }
}

27 de janeiro de 2011

Usar Helpers nas Actions (ex: url_for)

Para usar helpers em qualquer ficheiro da aplicação:

sfProjectConfiguration::getActive()->loadHelpers("Partial", "Url", "MyHelper");

Daqui.