Aplicativos em PHP/FrameWorks/P4A

P4A – PHP for Application

Site oficial - http://p4a.sf.net


P4A (PHP For Applications) é um framework em PHP tipo RAD e orientado a objetos para construção event-driven de aplicações web. Usa tableless HTML, suporte às teclas de atalho, point&click gerador de aplicativos, reconhecimento automático de datos, UTF-8, i18n/l10n, integração com a biblioteca PEAR.

De autoria do italiano Fabrizio Balliano.

Segundo a Wikipédia (http://pt.wikipedia.org):

RAD

Rapid Application Development (RAD), também conhecido como Desenvolvimento Rápido de Aplicação, é um modelo de processo de desenvolvimento de software iterativo e incremental que enfatiza um ciclo de desenvolvimento extremamente curto (entre 60 e 90 dias). O termo foi registrado por James Martin em 1991 e tem substituído gradativamente o termo de prototipação rápida que já foi muito utilizada no passado


Framework

No desenvolvimento do software, um framework é uma estrutura de suporte definida para que outro projeto de software possa ser organizado e desenvolvido. Tipicamente, um framework pode incluir programas de apoio, bibliotecas de código, linguagens de script e outros softwares para ajudar a desenvolver e juntar diferentes componentes de um projeto.


Elementos do P4A

Masks

É o objeto básico de interfaces, que contem todos os widgets e geralmente exibe objetos.

Localizado em /p4a/objects/mask.php (linha 45)

P4A_Object
   |
   --P4A_Mask


Widgets

São os elementos GUI prontos que podemos usar para compor nossas máscaras (masks). Classe base para objetos que permitem a usuários interagirem com a aplicação: botões, menus, .


Localizado em /p4a/objects/widget.php (linha 48)

P4A_Object
   |
   --P4A_Widget


O P4A é um projeto abrigado no SourceForge desde fevereiro de 2003, que chegou ao 12o. lugar no ranking geral dos mais de 100.000 projetos do SourceForge, tendo ganhado alguns prêmios neste repositório. Atualmente fica em primeiro lugar entre os frameworks de PHP no SourceForge.net.

Já conta com locales para uns 160 países e tradução para uns 60, inclusive o Brasil (pt_BR).


P4A é um framework PHP, um software contendo bibliotecas, módulos e controles (widgets) usados para a construção de aplicações que devem ser usadas com um web browser conectado à Internet. Com P4A você estará habilitado a desenvolver e escrever código de aplicações web como você faria com as ferramentas RAD mais evoluídas. O resultado deverá ser uma aplicação que o usuário deverá usar pensando que está usando uma aplicação cliente clássica (tipo desktop).

Você não deve se preocupar com a criação da interface gráfica para seus projetos, porque P4A já tem um tema gráfico cliente agradável.


Características

- Escrito em PHP (www.php.net), compatível com as versões PHP 4.3.x e 5.x.x e Apache 1.3.x e 2.0.x em Linux e Windows

- Integrado com a biblioteca Pear/DB (http://pear.php.net) e com isso suporta todos os bancos da suportados pela PEAD/DB: PostgreSQL, MySQL, SQL Server, Oracle e outros.

- Lógica e Design separados pelo template (http://pear.php.net/package/HTML_Template_Flexy)


Busca

Outras informações sobre o Projeto P4A:

- Home Page – http://p4a.sourceforge.net
- Downloads - http://sourceforge.net/project/showfiles.php?group_id=98294
- Demo online - http://p4a.sourceforge.net/demo
- Forum em italiano - http://sourceforge.net/forum/forum.php?forum_id=473897 
- Forum em inglês - http://sourceforge.net/forum/forum.php?forum_id=340765
- Quem está usando - http://p4a.sourceforge.net/who-s-using-p4a
- Referência de Código - http://p4a.sourceforge.net/code-reference
- Tutoriais - http://p4a.sourceforge.net/tutorials
- Widgets - http://p4a.sourceforge.net/widgets
- Icon Packs - http://p4a.sourceforge.net/icons-packs 
- Contribuições - http://p4a.sourceforge.net/info-about-contribs (Para instalar uma contribuição, basta descompactar no diretória da sua aplicação. 
Após instalar o contrib todos os objetos necessários ao P4A serão automaticamente incluídos na sua aplicação).
- Protótipo de Contribuição - http://p4a.sourceforge.net/contrib-prototype (protótipo vazio para ajudar pessoas a criarem uma nova contribuição).
- Logo mais abaixo da seção anterior (no site oficial) outros aplicativos de terceiros úteis.

Widgets

São os elementos GUI prontos que podemos usar para compor nossas máscaras (masks). Classe base para objetos que permitem a usuários interagirem com a aplicação: botões, menus, .

Esta lista (dos principais) é aninhada para reproduzir a herança das classes. Todos estes objetos extendem a classe P4A_Widget. Veja a referência de código para detalhes.

    * Button
    * Canvas
    * Field
    * Frame
          o Fieldset
    * Menu
    * Message
    * Table
    * Tab pane
    * Toolbar
          o Actions toolbar
          o Navigation toolbar
          o Simple toolbar
          o Standard toolbar
          o Quit toolbar


Desenvolvendo Aplicativos com o P4A


Pré-Requisitos:

- Sistemas Operacionais Linux ou Windows

- PHP 4.3.x/4.4.x/5.x.x com Apache 1.3.x/2.0.x

- A biblioteca PEAR/DB

- Um banco de dados suportado pela bibliteca acima, caso o seu aplicativo use banco


Quando instalamos o Xampp (http://xampp.sf.net), ele provê todos os requisitos do P4A, inclusive tem versão para ambos os sistemas operacionais.


Quando descompactamos o p4a ele cria a seguinte estrutura de diretórios:

p4a/applications
p4a/docs
p4a/icons
p4a/libraries
p4a/p4a
p4a/themes
p4a/index.php
p4a/p4a.php


Os aplicativos criados devem ficar na pasta “applications”.


Na pasta “docs” está toda a referência do P4A.


Em “icons” estão os ícones utilizados, em vários tamanhos.


A pasta “p4a” é a principal do framework. Nela estão as bibliotecas, os objetos, os locales e mais alguns componentes importantes.


Os temas devem ficar na pasta “themes”


Exemplo de Estrutura Mínima de uma Aplicação em P4A

<ola_mundo>
index.php
   <objects>
    ola_mundo.php
    ola_mascara.php

Caso a aplicação utilize algum upload deverá ter uma pasta extra “uploads”, contendo uma sub pasta “tmp” e ambas com permissão 777.

Permissões: os demais arquivos: index.php, ola_mundo.php e ola_mascara.php devem ter permissão de leitura, escrita e execução para o dono e leitura e execução para Outros.


Primeiro Exemplo – Olá Mundo

1)Criamos uma pasta ola_mundo dentro da pasta applications.

2)Criamos dentro de ola_mundo o arquivo index.php com o seguinte conteúdo:

index.php

<?php
 // Carregar a biblioteca do framework
require_once( dirname(__FILE__) . '/../../p4a.php' );
 
// Instanciar a classe que define a aplicação ola_mundo.
// A chasse encontra-se no diretório objects em um arquivo
// com o mesmo nome da classe
$app =& p4a::singleton("ola_mundo");
 
// Lançar a aplicação
$app->main();
?>


No arquivo index.php existe uma chamada para a classe principal do aplicativo que encontra-se no arquivo de mesmo nome (ola_mundo.php) dentro da pasta objects.

ola_mundo.php

<?php
 
// A classe principal da aplicação sempre estende a classe p4a do framework
class ola_mundo extends P4A{
	// Construtor da classe
	function ola_mundo ()	{
		// A primeira coisa a fazer, sempre é lançar o construtor da super classa p4a
		parent::p4a();
		
		// A seguir o código para executar, tipicamente abre a primeira máscara (masks)
		$this->openMask("mensagem");
	}
}
?>


Veja que a classe principal abre o arquivo de máscara (mensagem.php).

mensagem.php

<?php
 
// Para criar uma máscara, vem criar uma classe que herda da classe P4A_Mask.
class mensagem extends P4A_Mask{
	// Construtor da classe
	function mensagem()	{
		// Para a aplicação a primeira coisa que se lança é o construtor da classe pai
		parent::P4A_Mask();

		// Eles vem instanciar o widget para posicionar sobre a máscara usando o método
		// build da máscara (ou melhor, da classe P4A_Objects da qual tudo deriva
		
		// O objeto box é um Label de texto não modificável.
		// Uma vez instanciado o texto adota a mesma posição, formatável em HTML
		// acessável com $this->box
 
		$this->build("p4a_box","box");
		$this->box->setValue("Olá Mundo");
		
		// O objeto frame é um tipo de container disponível que 
		// colabora para o posicionamento relativo (sem tabela html) dos widget
		//em questão, caso se acesse o widget criado usando o apontador retornado com build
		$frm =& $this->build("p4a_frame","frame");
		$frm->anchorCenter($this->box);
		
		// Finalmente visualizamos o container (e o widget contido)
		// em uma de 3 seções do template (por default são top,menu,main)
		$this->display("main", $frm);
		
		$this->box->addAction("onClick");
		$this->intercept($this->box, "onClick", "change_box");
	}
	function change_box()
	{
		if ($this->box->getValue() == "Olá mundo") {
			$this->box->setValue("Clique para acessar");
		} else {  // Abaixo adiciona um ponto ao final 
			$this->box->setValue($this->box->getValue() . ".");
		}
	}
}
?>


A pasta objects pode ou não conter uma sub pasta “masks”. Caso criemos a pasta “masks”, ela deverá contr os arquivos de interface da aplicação (masks). Em termos de organização e entendimento da estrutura é adequada a criação da subpasta masks.

Experimente criar este pequeno exemplo com e sem a pasta masks. Nela apenas ficará o arquivo mensagem.php. Dica: instale a extensão “Web develop” no Firefox e sempre que houver algum erro, corrija o erro e clique em Cookies – Clear Session Cookies e só então clique no botão Refresh do browser.

Se no Internet Explorer, feche e abra o browser novamente após qualquer alteração, ou faça alguma configuração correspondento a da extensão do FF.


Segundo Exemplo – Acessando Banco de Dados

Este exemplo usa um banco de nome “p4a”, com o SGBD PostgreSQL. Teremos apenas uma tabela, funcionarios, com a estrutura abaixo:

CREATE TABLE funcionarios (
    codigo integer NOT NULL,
    nome character varying(40),
    siape character varying(10),
    foto text,
    observacao text,
    primary key (codigo)
);


A estrutura do aplicativo será:

<funcionarios>
	index.php
	<objects>
		cadastro.php
		funcionarios.php
	<uploads>	(Com permissão de escrita - 777>
		<tmp> (Com permissão de escrita - 777>

Arquivos:

index.php

<?php

// Ampliar retornos de erro do P4A
define("P4A_EXTENDED_ERRORS", 'TRUE');

// Define locales para pt_BR. Muito útil para o usuário este recurso, já que campos:
// numéricos receberão a máscara 1.234,56 e campos data
// 25/12/2005 além de outras facilidades
define("P4A_LOCALE", 'pt_BR');

// Define dados da conexão ao banco
define("P4A_DSN", 'pgsql://postgres:suasenha@127.0.0.1:5432/p4a');

//define("P4A_DSN", 'mysql://root:@127.0.0.1/p4a');
//Incluir lib P4A
require_once( dirname(__FILE__) . '/../../p4a.php' );

/*
Now we instance the application with the singleton
method, than we call the "main" method. main is executed
every page call (click and reload included).
The application must be a class under the "objects" directory
and have to extend "p4a" class.
Attention, in p4a all object have to be assigned using the "=&"
operator or you will loose all references. You also have to
use the "&" operator in the method definition, when the method
returns objects (also for the class constructor).
Take a look at "cadastro" class for a better understanding.
*/

// Checar instalação e configuração.
// As linhas abaixo devem ser removidas após a primeira execução
/*
$check = p4a_check_configuration();

// Here we go
if (is_string($check)) {
	print $check;
} else {
	$p4a->main();
}
*/
$app =& p4a::singleton("cadastro");
$app->main();
?>

objects/cadastro.php

<?php
 // A classe da aplicação sempre estende a classe p4a do framework
class cadastro extends P4A{
	// Construtor da classe
	function cadastro()	{	
		// A primeira coisa a fazer sempre é lançar o construtor da classe p4a		
		parent::p4a();

		// Construção do menu
		$this->build("p4a_menu", "menu");
		$this->menu->addItem("cadastro", "C&adastro");
	
		$this->menu->items->cadastro->addItem("funcionarios","Funcionários");
		$this->intercept($this->menu->items->cadastro->items->funcionarios,
						"onClick", "menuClick");

		//Em ... addItem("funcionarios","Funcionários"), 
		// funcionarios é a classe e Funcionários o label.

		$this->menu->items->cadastro->addItem("funcionarios2","Funcionários2");
		$this->intercept($this->menu->items->cadastro->items->funcionarios2,"onClick",
							 "menuClick");

		// Código da execução, tipicamente abre a primeira máscara (mask)
		$this->openMask("funcionarios");
	}

	function menuClick()	{
		$this->openMask($this->active_object->getName());
	}
}
?>

objects/funcionarios.php

<?php
// Para criar uma máscara, cria uma classe que herda da classe P4A_Mask
class Funcionarios extends P4A_Mask{
	// Construtor da classe funcionarios
	function Funcionarios()	{
		//Para a aplicação a primeira coisa se lança o costrutor da classe pai
		$this->p4a_mask();
		$p4a =& p4a::singleton();

		// DB Source
		$this->build("p4a_db_source", "source");
		/*$this->source->setFields(array("funcionarios.*" => "*"));*/
		$this->source->setTable("funcionarios");
		$this->source->setPk("codigo");
		$this->source->addOrder("nome");
		$this->source->setPageLimit(10);
		$this->source->load();
		//$this->source->fields->codigo->setSequence("funcionarios");
		$this->source->fields->codigo->setSequence("codigo");

		$this->setSource($this->source);
		$this->source->firstRow();

		// Personalizar propriedades dos campos (vide função abaixo)
		$this->setFieldsProperties();
		$fields =& $this->fields;

		// Busca em registros
		$fs_search =& $this->build("p4a_fieldset","fs_search");
		$fs_search->setTitle("LocalizaçÃo de Funcionários");
		$txt_search =& $this->build("p4a_field", "txt_search");
		$txt_search->addAction("onReturnPress");
		$this->intercept($txt_search, "onReturnPress","search");
		$txt_search->setLabel("Nome ou parte");
		$cmd_search =& $this->build("p4a_button","cmd_search");
		$cmd_search->setValue("Ok");
		$this->intercept($cmd_search, "onClick","search");
		$fs_search->anchor($txt_search);
		$fs_search->anchorLeft($cmd_search);

		// Toolbar
		$this->build("p4a_standard_toolbar", "toolbar");
		$this->toolbar->setMask($this);

		// Tabela (exibe funcionários, um registro por linha)
		$table =& $this->build("p4a_table", "table");
 		$table->setWidth(725);
		$table->setSource($this->source);
		$table->setVisibleCols(array("codigo","nome","siape"));
		$table->cols->codigo->setLabel("Código");		

		while ($col =& $table->cols->nextItem()) {
			$col->setWidth(150);
		}
		$table->showNavigationBar();

		// Mensagem de erro
		$message =& $this->build("p4a_message", "message");
		$message->setWidth("300");

		//Fieldset con l'elenco dei campi
		$fset=& $this->build("p4a_fieldset", "frame");
		$fset->setTitle("Cadastro do Funcionário");

 		$fset->anchor($this->fields->codigo);
		$fset->anchor($this->fields->nome);
		$fset->anchor($this->fields->siape);
		$fset->anchor($this->fields->foto);
		$fset->anchor($this->fields->observacao);

		// Frame (abriga mensagem, tabela, busca e campos)
		$frm=& $this->build("p4a_frame", "frm");
		$frm->setWidth(730);
		$frm->anchor($fs_search);
		$frm->newRow();
		$frm->anchorCenter($message);
		$frm->anchor($table);
  		$frm->anchor($fset);

		// Campos requeridos
	    $this->mf = array("codigo", "nome", "siape");
		foreach($this->mf as $mf){
			$fields->$mf->label->setFontWeight("bold");
		}

		// Exibir controles (Display)
		$this->display("main", $frm);
		$this->display("menu", $p4a->menu);
		$this->display("top", $this->toolbar);
	}

	function main()	{
		parent::main();
		foreach($this->mf as $mf){
			$this->fields->$mf->unsetStyleProperty("border");
		}
	}
	// Função que personaliza propriedades dos campos
	function setFieldsProperties()	{
		$p4a =& p4a::singleton();
		$fields =& $this->fields;

		$fields->codigo->setLabel("Código");
		$fields->codigo->setWidth(200);
		$fields->codigo->enable(false);

		$fields->nome->setWidth(400);

		$fields->siape->setLabel("SIAPE");
		$fields->siape->setWidth("400");

		$fields->foto->setType("image");

		$fields->observacao->setType("rich_textarea");
		$fields->observacao->enableUpload();
	}
	// Função que salva os registros
	function saveRow()	{
		$valid = true;

		foreach($this->mf as $mf){
			$value = $this->fields->$mf->getNewValue();
			if(trim($value) === ""){ // Abaixo, em vermelho as bordas dos campos
				$this->fields->$mf->setStyleProperty("border", "1px solid red");
				$valid = false;
			}
		}

		if ($valid) {
			parent::saveRow();
		}else{
			$this->message->setValue("Favor preencher todos os campos requeridos!");
		}
	}
	// Função que implementa a busca
	function search()	{
		$value = $this->txt_search->getNewValue();
		$this->data->setWhere("nome ILIKE '%{$value}%'"); // Busca case-insensitive
		$this->data->firstRow();
		$num_rows = $this->data->getNumRows();

		if (!$num_rows) {
			$this->message->setValue("Nenhum nome encontrado contendo: $value!");
			$this->data->setWhere(null);
			$this->data->firstRow();
		}
	}
}
?>


Exemplo Com 3 Tabelas

Este é um exemplo que acompanha o P4A original,contendo 3 tabelas, com exemplos de joins e que roda bem no MySQL. No PostgreSQL, devido a algumas incompatibilidades com a PEAR/DB, ele não roda bem. Segundo o autor do P4A, Fabrizi, esta incompatibilidade é apenas com os joins.

A estrutura das tabelas é:

CREATE TABLE brands (
  brand_id INTEGER NOT NULL,
  description TEXT NOT NULL,
  visible BOOL NOT NULL,
  PRIMARY KEY(brand_id)
);

CREATE TABLE categories (
  category_id INTEGER NOT NULL,
  description TEXT NOT NULL,
  visible BOOL NOT NULL,
  PRIMARY KEY(category_id)
);

CREATE TABLE products (
  product_id VARCHAR(50) NOT NULL,
  brand_id INTEGER NOT NULL,
  category_id INTEGER NOT NULL,
  model TEXT NOT NULL,
  purchasing_price DECIMAL(10,2) NOT NULL,
  selling_price DECIMAL(10,2) NOT NULL,
  discount INTEGER NOT NULL,
  little_photo TEXT NULL,
  big_photo TEXT NULL,
  is_new BOOL NOT NULL,
  visible BOOL NOT NULL,
  description TEXT NOT NULL,
  PRIMARY KEY(product_id)
);


Crie o banco p4a no MySQL com as tabelas acima.

A estrutura dos arquivos da aplicação:

<products_catalogue>
	index.php
	<objects>
		products_catalogue.php
		products.php
		categories.php
		brands.php
	<uploads>	(Com permissão de escrita - 777>
		<tmp> (Com permissão de escrita - 777>


Observe que:

index.php em $p4a =& p4a::singleton("products_catalogue") chama a classe products_catalog no arquivo de mesmonome.

products_catalog.php em $this->openMask("products") chama a classe products no arquivo products.php

Através do menu se tem acesso as demais classes dos arquivos brands.php e categories.php.

O código do aplicativo acompanha o P4A, portanto não há necessidade de colar aqui, somente colarei alguns trechos para comentar:


objects/products_catalogue.php

Neste arquivo encontramos este trecho de código:

		// Data sources
		$this->build("p4a_db_source", "brands");
		$this->brands->setTable("brands");
		$this->brands->setPk("brand_id");
		$this->brands->addOrder("description");
		$this->brands->load();
		$this->brands->fields->brand_id->setSequence("brands");

		$this->build("p4a_db_source", "categories");
		$this->categories->setTable("categories");
		$this->categories->setPk("category_id");
		$this->categories->addOrder("description");
		$this->categories->load();
		$this->categories->fields->category_id->setSequence("categories");


Este diz quais as duas outras tabelas (db_sources) que serão utilizadas no aplicativo.

Trecho de “objects/products.php” definindo uma combo com acesso ao banco:

		$fields->brand_id->setLabel("Brand");
		$fields->brand_id->setWidth(200);
		$fields->brand_id->setType("select");
		$fields->brand_id->setSource($p4a->brands);
		$fields->brand_id->setSourceDescriptionField("description");

O index.php deve ficar parecido com este. Ajuste para os dados do seu MySQL:

<?php
// Caso apareça algum erro, é útil descomentar alinha abaixo para melhor debug
//define("P4A_EXTENDED_ERRORS", 'TRUE');
define("P4A_LOCALE", 'en_US');
define("P4A_DSN", 'mysql://root:@localhost/p4a');
//define("P4A_DSN", 'pgsql://postgres:postabir@localhost:5432/p4a');

require_once dirname(__FILE__) . '/../../p4a.php';

// Check Installation and configuration.
// This lines should be removed after the first run.
$p4a =& p4a::singleton("products_catalogue");
$check = p4a_check_configuration();

// Here we go
if (is_string($check)) {
	print $check;
} else {
	$p4a->main();
}
?>


Dicas Extras

Suporte ao Ajax

A versão 1.99.2 já traz suporte ao Ajax. Basta ver o calendário adicionado ao lado dos campos data.


Acentuação

Quando à acentuação vale alertar que a codificação do P4A é a UTF8. Caso tenha algum problema com acentuação deverá encontrar um editor com suporte a essa codificação para corrigir os acentos, como é o caso dos editores do Linux gEdit e Kate.


Dicas do forum do projeto (no sourceForge.net).

Tenho duas tabelas. Quando clicar no botão Save, na Toolbar, quero salvar as duas tabelas.

R - Sobrescreva o método saveRow() na sua mask. Se você tem 2 db_sources você pode fazer:

parent::saveRow(); $outro_db_source->saveRow();

E assim por diante.


- Eu tenho duas mask e preciso compartilhar dados entre ambas. Ao clicar no registro eu preciso abrir a outra mask

R – Você pode acessar dados de qualquer mask com: $p4a->masks->nomemask->desejado


Como interceptar o método Save na Toolbar?

R - $this->intercept($toolbar->button->save, “onClick”, “method”)

Como inserir dados?

R- Você podecriar um db_source e fazer e circular por todos os elementos que você deseja inserir

$db_source->newRow();
$db_source->setValue(“valor”);
$db_source->saveRow();

Como alterar Labels?

R -

$this->fields->fieldname->setLabel(“Meulabel”);

$table->cols->nomecoluna->setLabel(“Meulabel”);


Como exibir em um campo data a data de hoje por default?

R-

$source->fields->order_date->setDefaultValue(P4A_Date::now(P4A_DATE));

Ou

$format = '%d/%m/%Y-%H/%M/%S'; // Ou outro formato

.

.

P4A_Date::now( $format );


Instalando Novos Temas no P4A

Acesse a página - http://p4a.sourceforge.net/icons-packs

E faça o download do tema desejado. Veja que para instalar basta editar a index.php do P4A e adicionar a linha:

define("P4A_ICONS_PATH", "/path/to/the/icons/directory");

Atualmente (06/2007) o P4A está na versão estável 2.0.6 e 2.1.3 em desenvolvimento.