Sunteți pe pagina 1din 43

Dominando

Flex e PHP
Livro Online Esta a continuao do livro fsico Dominando Flex e PHP, na qual comeamos a criar o sistema que gerencia um consultrio mdico. imprescindvel o total entendimento de todos os captulos do livro (principalmente o captulo 5), para que possamos dar prosseguimento a verso digital.

www.danielschmitz.com.br suporte@danielschmitz.com.br

ESTRATGIA ADOTADA NO DESENVOLVIMENTO DO SISTEMA


Um sistema complexo pode ser dividido em partes menores, e na parte digital do nosso livro iremos adotar esta estratgia, criando cada tela e cada classe por vez. Ou seja, no vamos apresentar o cdigo de todas as classes de negcio para consulta, depois todos os formulrios etc, vamos criar as classes na medida em que formos construindo o sistema. Tambm no ser uma boa estratgia de aprendizagem utilizar o cdigo fonte pronto, disponvel para download no nosso site. Se voc deseja realmente aprender flex e php, recomendamos que escreva todas as classes e funcionalidades existentes (apesar do cdigo fonte estar disponvel para download).
MAPEAMENTO DAS TABELAS ENTRE ESPECIALIDADE E PROFISSIONAL

Todas as tabelas que esto relacionadas na contra capa do livro fsico devem ter os seus mapeamentos criados para que possamos fazer operaes no banco sem a necessidade inicial do uso de joins. Todas as tabelas herdam da classe TableBase, que a princpio est vazia, mas podem ter funcionalidades adicionadas ao longo do desenvolvimento do sistema. /consultorio/library/TableBase.php
<?php abstract class TableBase extends Zend_Db_Table_Abstract { }

Vamos comear o mapeamento pelas tabelas mais fceis. A tabela Especialidades possui apenas dois campos, e um relacionamento com Profissionais. Na verdade, a tabela de especialidades no possui um relacionamento, mas apenas uma dependncia, pois a chave estrangeira est na tabela de profissionais. /consultorio/library/EspecialidadesTable.php
<?php class EspecialidadesTable extends TableBase { protected $_name = "especialidades"; protected $_primary = "id";

protected $_dependentTables = array("ProfissionaisTable"); }

/consultorio/library/ProfissionaisTable.php
<?php class ProfissionaisTable extends TableBase { protected $_name="profissionais"; protected $_depends="AgendasTable"; protected $_referenceMap=array( "EspecialidadesTable" => array( "columns"=>"idEspecialidade", "refTableClass"=>"EspecialidadesTable", "refColumns"=>"id", "onDelete"=>self::RESTRICT ) ); }

Como a tabela de Profissionais possui idEspecialidade, preciso criar uma entrada no referenceMap da classe ProfissionaisTable. Dessa forma, indicamos a relao entre as tabelas de Profissionais e Especialidades, sem preocupar com a cardinalidade da mesma. Com isso podemos, ao obter um registro da tabela de profissionais, obter a especialidade correspondente, sem o uso de joins.

AMPLIANDO FUNCIONALIDADES NA CLASSE SERVICEBASE


Ampliando a classe ServiceBase, temos: /consultorio/services/ServiceBase.php
<?php abstract class ServiceBase { /** * A classe filha deve fornecer a classe Abstract Table */ protected $_tableClassName; /** * A classe filha deve fornecer a classe VO */ protected $_voClassName;

/** * * A instncia da Tabela * @var TableBase tbl */ private $tbl; public function __construct() { //instancio dinamicamente a TableClass if ($this->_tableClassName!=null) $this->tbl = new $this->_tableClassName; } /** * Usado para adicionar aspas em textos que * forem digitados. Bom para evitar SQL Injection * @param string $text * @param string $value */ protected function quote($text, $value) { return Zend_Db_Table::getDefaultAdapter()->quoteInto($text, $value); } /** * Obtm um object VO dado um ID * @param integer $id */ protected function getVOById($id) { $row = $this->getRowByID($id); return $this->getVOByRow($row); } /** * Obtm um Zend_Db_Table_Row dado um ID * @param int $id */ protected function getRowById($id) { return $this->tbl->find($id)->current(); } /** * retorna um objeto VO dado um Row * @var Zend_Db_Table_Row_Abstract $row */ protected function getVOByRow(Zend_Db_Table_Row_Abstract $row) { $vo = new $this->_voClassName; foreach ($vo as $key => $value) {

if ($row->__isset($key)) $vo->$key = $row->$key; } return $vo; } /** * Obtm todos os registros da tabela * @param string $where */ protected function getAll($where=null) { $all = $this->tbl->fetchAll($where); return $all; } /** * Salvar objeto. Pode ser insert/update * @param object $vo */ protected function save($vo) { if ($vo->id != null) {//update $where = $this->quote("id=?", $vo->id); $this->tbl->update(get_object_vars($vo), $where); } else {//insert $this->tbl->insert(get_object_vars($vo)); } } /** * Apagar um objeto * @param int $id */ public function del($id) { $row = $this->tbl->find($id)->current(); if ($row) $row->delete(); else throw new Zend_Exception("Este objeto no existe"); } }

Na classe ServiceBase criamos alguns mtodos que sero usados por todas as classes que acessam as tabelas do sistema. Em conjunto, o ServiceBase usa na varivel $this->tbl a instncia de uma classe que da classe TableBase, como por exemplo EspecialidadesTable.

Mtodos como save, del e getAll cumprem o papel de uma tela CRUD do sistema. Alm disso, tambm temos o mtodo getVOById, que retorna uma classe VO e getRowById, que retorna um registro (Zend_Db_Table_Row) da tabela do banco de dados.

CLASSE ESPECIALIDADESSERVICE
Com a classe ServiceBase pronta, podemos agora criar a classe EspecialidadeService, de acordo com o cdigo a seguir: /consultorio/services/EspecialidadeService.php
<?php class EspecialidadeService extends ServiceBase { protected $_tableClassName = "EspecialidadesTable"; protected $_voClassName = "EspecialidadeVO"; /** * @var integer id * @return EspecialidadeVO */ public function getVOById($id) { return parent::getVOById($id); } /** * @return array[EspecialidadeVO] */ public function getAll() { $all = parent::getAll(); $especialidades = array(); foreach ($all as $row) { $especialidades[] = $this->getVOByRow($row); } return $especialidades; } /** * @var EspecialidadeVO * @return boolean */ public function save(EspecialidadeVO $vo)

{ parent::save($vo); return true; } /** * * @var int id */ public function del($id) { parent::del($id); return true; } }

Analisando a classe, podemos perceber que criamos novamente os mtodos save, del, gelAll etc. Veja que todos os mtodos criados fazem o que chamamos de polimorfismo (override). Ou seja, criamos um mtodo que possui a mesma assinatura do mtodo da classe pai e o reimplementamos, usando parent::nome_do_metodo para chamar a funcionalidade do mtodo da classe pai. Porque fizemos isso? Unicamente para garantir que os tipos envolvidos na classe EspecialidadeService so do tipo EspecialidadeVO, e no tipos genricos. Com isso, ao espelhar a classe para o Flex, os tipos sero convertidos corretamente. Logo no incio da classe temos duas variveis (tableClassName e voClassName) que correspondem s classes que representam a tabela e a classe VO do contexto. A classe EspecialidadeVO apresentada a seguir. /consultorio/library/EspecialidadeVO.php
<?php class EspecialidadeVO { /** * @var int */ public $id; /** * @var string */ public $nome; }

Aps criar as classes, vamos exp-las ao Flex. Para isso, e voc j deve ter feito esta operao algumas vezes, clique com o boto direito do mouse em EspecialidadeService.php e escolha o item Create PHP Service for Flex. Antes de continuar, leia o texto a seguir Ateno! Durante o desenvolvimento das classes eu tive um problema srio com o Flash Builder for PHP, que criava as classes VO fora do diretrio valueObjects, que deveria ser o diretrio padro. Mesmo indicando este diretrio, as classes eram criadas em uma pasta com o mesmo nome do projeto. Este Bug est devidamente documentado neste endereo: http://bugs.adobe.com/jira/browse/FB-32337 Por enquanto, ns vamos criando os valueObjects consultorioFlex, que o nome do projeto flex: na pasta

CLASSE PROFISSIONAISSERVICE
Seguindo a mesma idia do EspecialidadeService, temos a classe ProfissionaisService, veja: /consultorio/services/ProfissionaisService.php
<?php class ProfissionaisService extends ServiceBase { protected $_tableClassName = "ProfissionaisTable"; protected $_voClassName = "ProfissionalVO"; /** * @var EspecialidadeService */ protected $especialidadeService; public function __construct() { $this->especialidadeService = new EspecialidadeService(); parent::__construct(); } /** * @var integer id * @return ProfissionalVO */ public function getVOById($id) { $row = $this->getRowById($id); $especialidade = $row->findParentRow("EspecialidadesTable"); $vo = $this->getVOByRow($row); $vo->especialidade = $this->especialidadeService>getVOByRow($especialidade); return $vo; } /** * @return array[ProfissionalVO] */ public function getAll() { $all = parent::getAll(); $profissionais = array(); foreach ($all as $row)

{ $profissionais[] = $this->getVOByRow($row); } return $profissionais; } /** * @var ProfissionalVO */ public function save(ProfissionalVO $vo) { $vo->idEspecialidade = $vo->especialidade->id; // tenho que retirar esta propriedade para o save funcionar unset($vo->especialidade); parent::save($vo); } }

Como

possui uma especialidade, a classe ProfissionaisService possui um relacionamento com EspecialidadeService, j que quando chamarmos o mtodo getAll(), tambm teremos uma referncia a especialidade. Inclusive o mtodo save retira essa referncia, para que o insert/update seja realizado corretamente. Veja que, na classe ProfissionalVO, temos uma referncia tambm para EspecialidadeVO: /consultorio/library/ProfissionalVO.php
<?php class ProfissionalVO { /** * @var int */ var $id; /** * @var int */ var $idEspecialidade; /** * @var string */ var $nome; /** * @var string */ var $login; /**

um

Profissional

* @var string */ var $senha; /** * @var string */ var $sexo; /** * @var string */ var $conselhoProfissional; /** * @var string */ var $numeroConsProfissional; /** * @var string */ var $uf_consProfissional; /** * @var string */ var $end_logradouro; /** * @var string */ var $end_numero; /** * @var string */ var $end_complemento; /** * @var string */ var $end_bairro; /** * @var string */ var $end_cidade; /** * @var string */ var $end_estado; /** * @var string */ var $end_cep; /** * @var string */ var $tel_residencial; /** * @var string */ var $tel_celular;

/** * @var string */ var $tel_comercial; /** * @var string */ var $email; /** * @var EspecialidadeVO */ var $especialidade; }

Essa referncia a especialidade preenchida no Flex, e ser vista logo a seguir. Aps criar as classes ProfissionaisService e ProfissionalVO, podemos exp-la ao Flex, atravs do Create PHP Service for Flex:

Como pode-se perceber, as classes services at agora contm apenas mtodos bsicos relacionados ao CRUD de dados. Mtodos mais complexos sero realizados na medida em que o sistema desenvolvido. Agora que temos o bsico da parte PHP, vamos criar o bsico na parte Flex, que corresponde a tela principal da aplicao e as telas crud do sistema.

CRIANDO OS CONTROLLERS DE ESPECIALIDADES E PROFISSIONAIS


Seguindo o que foi discutido no captulo 5, criaremos controllers que correspondero a manipulao de dados, enquanto que as views do sistema vo usar esta manipulao, separando assim a aplicao em camadas. Alm disso, tambm criamos eventos que nos ajudam a fazer a comunicao entre as diversas partes do sistema. Vamos s classes: /consultorioFlex/src/events/EspecialidadesEvent.as
package events { import flash.events.Event; public class EspecialidadesEvent extends Event { public static const SAVE:String = "EspecialidadesEvent.SAVE"; public static const DEL:String = "EspecialidadesEvent.DEL"; public static const GETALL:String = "EspecialidadesEvent.GETALL"; public static const POSTCONSTRUCT:String = "EspecialidadesEvent.POSTCONSTRUCT"; public static const CREATE:String = "EspecialidadesEvent.CREATE"; public function EspecialidadesEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) { super(type, bubbles, cancelable); } } }

/consultorioFlex/src/events/ProfissionaisEvent.as
package events { import flash.events.Event;

public class ProfissionaisEvent extends Event { public static const SAVE:String = "ProfissionaisEvent.SAVE"; public static const DEL:String = "ProfissionaisEvent.DEL"; public static const GETALL:String = "ProfissionaisEvent.GETALL"; public static const POSTCONSTRUCT:String = "ProfissionaisEvent.POSTCONSTRUCT"; public static const CREATE:String = "ProfissionaisEvent.CREATE"; public function ProfissionaisEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) { super(type, bubbles, cancelable); } } }

Os eventos possuem referencia s operaes crud, para que a camada de visualizao (View) seja notificada sobre atualizaes. /consultorioFlex/src/controllers/EspecialidadesController.as
package controllers { import consultorioFlex.EspecialidadeVO; import events.EspecialidadesEvent; import mx.collections.ArrayCollection; import mx.collections.ArrayList; import mx.rpc.events.ResultEvent; import services.especialidadeservice.EspecialidadeService; public class EspecialidadesController extends ControllerBase { /** * Possui um array de especialidades */ [Bindable] public var dataProvider:ArrayCollection; /** * Especialidade atual selecionada no dataProvider */ [Bindable] public var current:EspecialidadeVO;

/** * Service injetado do Bean */ [Inject] public var especialidadeService:EspecialidadeService; [PostConstruct] public function onPostConstruct():void { } public function getAll():void { this.serviceHelper.executeServiceCall(especialidadeService.getAll(),on GetAll); } protected function onGetAll(event:ResultEvent):void { this.dataProvider = event.result as ArrayCollection; dispatcher.dispatchEvent(new EspecialidadesEvent(EspecialidadesEvent.GETALL)); } public function save():void { this.serviceHelper.executeServiceCall(especialidadeService.save(curren t),OnSave); } public function OnSave(event:ResultEvent):void { this.current = new EspecialidadeVO(); this.dispatcher.dispatchEvent(new EspecialidadesEvent(EspecialidadesEvent.SAVE)); } public function del():void { this.serviceHelper.executeServiceCall(especialidadeService.del(current .id),onDel); } public function onDel(event:ResultEvent):void { this.current = new EspecialidadeVO(); this.dispatcher.dispatchEvent(new EspecialidadesEvent(EspecialidadesEvent.DEL)); } public function create():void

{ this.current = new EspecialidadeVO(); this.dispatcher.dispatchEvent(new EspecialidadesEvent(EspecialidadesEvent.CREATE)); } public function getById(idEspecialidade:int):EspecialidadeVO { for each(var especialidade:EspecialidadeVO in this.dataProvider) { if (especialidade.id == idEspecialidade) return especialidade; } return null; } } }

Quase todo controller ter duas variveis que sero muito usadas na camada de visualizao. So elas: dataProvider e current. A primeira armazena uma lista de registros que geralmente so exibidos em um DataGrid. Repare que, como no estamos trabalhando com a View, o dataProdiver pode ser usado tanto para um DataGrid quanto para um ComboBox ou um List. Neste momento, isso pouco importa. A segunda, current, contm uma referncia para um registro apenas, geralmente o que est sendo editado naquele mmento. No mtodo gelAll, usamos a classe EspecialidadeService, que foi previamente criada pelo Flash Builder e est devidamente injetada no AppBeam.mxml (que vamos comentar logo a seguir). Aps carregar os dados, disparamos o evento EspecialidadesEvent.GETALL, executado no mtodo OnGetAll. Mas onde esse evento ser escutado? Sinceramente eu no sei ainda, mas sabemos que quando o controller de especialidades recarregar seu dataProvider, esse evento ser disparado. O mesmo acontece para outros mtodos, como Save e Del. /consultorioFlex/src/controllers/ProfissionaisController.as
package controllers { import consultorioFlex.ProfissionalVO; import events.ProfissionaisEvent;

import mx.collections.ArrayCollection; import mx.rpc.events.ResultEvent; import services.especialidadeservice.EspecialidadeService; import services.profissionaisservice.ProfissionaisService; public class ProfissionaisController extends ControllerBase { [Bindable] public var dataProvider:ArrayCollection; [Bindable] public var current:ProfissionalVO; [Inject] public var profissionaisService:ProfissionaisService; [Inject] public var especialidadeController:EspecialidadesController; public function getAll():void { this.serviceHelper.executeServiceCall(profissionaisService.getAll(),on GetAll); } public function onGetAll(event:ResultEvent):void { this.dataProvider = event.result as ArrayCollection; this.setEspecialidades(); dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.GETALL)); } public function save():void { this.serviceHelper.executeServiceCall(profissionaisService.save(curren t),onSave); } public function onSave(event:ResultEvent):void { this.current = new ProfissionalVO(); dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.SAVE)); } public function del():void { this.serviceHelper.executeServiceCall(profissionaisService.del(current .id),onDel);

} public function onDel(event:ResultEvent):void { this.current = new ProfissionalVO;; this.dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.DEL)); } public function create():void { this.current = new ProfissionalVO; this.dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.CREATE)); } public function setEspecialidades():void { /* Neste momento, eu vou descobrir a especialidade de cada profisisonal. A princpio eu tenho somente o ID, mas vou chamar uma funo que ir me retornar o objeto especialidade. */ for each (var profissional:ProfissionalVO in this.dataProvider ) { profissional.especialidade = especialidadeController.getById(profissional.idEspecialidade); } } } }

Veja

ProfissionaisController bem semelhante ao EspecialidadesController, exceto pelo mtodo setEspecialidades que

que

descobre a especialidade de cada profissional de acordo com o campo idEspecialidade.

CRIANDO A TELA PRINCIPAL DO SISTEMA


A tela principal do sistema conter um ButtonBar com as operaes mais importantes que so relacionadas a uma clnica, como por exemplo marcar uma consulta e consultar a agenda de um mdico. Este ButtonBar estar ligado a um ViewStack, que contm as telas para acesso imediato. Alm das telas principais, temos um menu de telas secundrias, com operaes CRUD.

No vamos caprichar no design agora, somente depois com o uso de skins. A tela principal tambm carrega o formulrio de login, que um popup.

Antes de exibir o cdigo da tela principal, temos que analisar a implementao de uma classe controller que ir gerenciar a aplicao. Vimos no captulo 5 que o Swiz implementa controllers que injetam dependncias por toda a aplicao. Vamos usar este benefcio para trazer um controller principal para a aplicao, no qual vamos chamar de AppController. A classe AppController ter a responsabilidade de obter as principais informaes sobre a aplicao, que ficam disponveis globalmente. Um

exemplo seria a lista de especialidades e profissionais do sistema, bem como o usurio logado e outros dados.

CLASSE APPCONTROLER
Esta classe dever ser criada em src/controllers, e injetada no AppBean.mxml da aplicao. /consultorioFlex/src/controllers/AppController.as
package controllers { import beans.AppBean; import consultorioFlex.AppDataVO; import events.AppEvent; import flash.events.Event; import mx.collections.ArrayCollection; import mx.collections.ArrayList; import mx.rpc.events.ResultEvent; import services.appservice.AppService; import consultorioFlex.AppDataVO; import consultorioFlex.EspecialidadeVO; /** * Controle funcionalidades gerais de toda a aplicao */ public class AppController extends ControllerBase { [Inject] public var appService:AppService; [Inject] [Bindable] public var especialidadesController:EspecialidadesController; [Inject] [Bindable] public var profissionaisController:ProfissionaisController; [PostConstruct] public function OnPostConstruct():void { }

public function loadAllData():void { this.serviceHelper.executeServiceCall(appService.loadAllData(),OnLoadD ata); } public function OnLoadData(event:ResultEvent):void { var dados:AppDataVO = event.result as AppDataVO; if (dados.especialidades!=null) especialidadesController.dataProvider = dados.especialidades as ArrayCollection; if (dados.profissionais!=null) { profissionaisController.dataProvider = dados.profissionais as ArrayCollection; profissionaisController.setEspecialidades(); } } public function doLogin(login:String, senha:String):void { //todo: encriptar senha this.serviceHelper.executeServiceCall(appService.doLogin(login,senha), onDoLogin); } private function onDoLogin(event:ResultEvent):void { if (event.result == true) { this.dispatcher.dispatchEvent(new AppEvent(AppEvent.LOGIN_OK)); } } } }

Veja

que o AppController faz referncia as classes EspecialidadesController e ProfissionaisController. No mtodo loadAllData, fazemos atravs de uma nica requisio ao servidor, a consulta de diversos dados que devem ser carregados. Por exemplo, a lista de especialidades deve ser carregada uma nica vez, e ficar disponvel para todo o sistema. O mesmo acontece com a lista de profissionais do sistema, e com outras informaes que iremos adicionar ao longo do sistema.

Repare que a classe AppController tem apenas a funcionalidade de acessar os servidor e trazer dados, repassando-os para os seus respectivos controllers. Os dados em si esto no controller responsvel. /consultorio/services/AppService.php
<?php /* * Service da aplicao global, com funcionalidades * extras que no esto ligadas as tabelas do sistema. */ class AppService { /** * Retorna um objeto genrico com dados * que so carregados no incio da aplicao, * que ficam disponveis em todo o momento da aplicao. * @return AppDataVO */ public function loadAllData() { $dados = new AppDataVO(); $especialidadesService = new EspecialidadeService(); $profissionaisService = new ProfissionaisService(); $dados->especialidades = $especialidadesService->getAll(); $dados->profissionais = $profissionaisService->getAll(); return $dados; } /** * * Enter description here ... * @var string $login * @var string $senha * @return boolean */ public function doLogin($login, $senha) { //todo... return true; } }

A classe AppService.php usa outros services para obter os dados de Especialidade e Profissional. Tambm criamos o mtodo doLogin que, a

princpio, retorna sempre verdadeiro. Vamos implementar o login nos prximos captulos. O mtodo loadAllData retorna um objeto do tipo AppDataVO, exibido a seguir: /consultorio/library/AppDataVO.php
<?php class AppDataVO { /** * @var array[EspecialidadeVO] */ public $especialidades; /** * @var array[ProfissionalVO] */ public $profissionais; }

Ao criar a classe AppService, no esquea de exp-la ao Flex para que a classe AppService.as seja criada, e lembre-se de definir o diretrio consultorioFlex para o Data Type. Com algumas partes construdas, podemos exibir agora o cdigo fonte da pgina principal. /consultorioFlex/src/consultorioFlex.mxml
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:swiz="http://swiz.swizframework.org" xmlns:beans="beans.*" controlBarVisible="true" xmlns:views="views.*" creationComplete="OnCreationComplete(event)" > <fx:Style source="assets/styles/app.css"/> <fx:Script> <![CDATA[ import controllers.AppController;

import mx.events.FlexEvent; import mx.events.MenuEvent; import mx.managers.PopUpManager; import spark.components.TitleWindow; import views.Especialidades; import views.Login; import views.Profissionais; [Inject] public var appController:AppController; protected function OnMenuItemClick(event:MenuEvent):void { var tela:TitleWindow; //Fazer um switch nao melhor opo //quando se tem muitas telas, mas //aceitavel at 10 telas... switch (event.label) { case "Profissionais": tela = new Profissionais(); break; case "Especialidades": tela = new Especialidades(); break; } PopUpManager.addPopUp(tela,this,true); PopUpManager.centerPopUp(tela); } /** * Executado ao carregar a aplicao. Deve exibir a tela de * login. */ public function OnCreationComplete(event:FlexEvent):void { var tela:Login = new Login(); PopUpManager.addPopUp(tela, this,true); PopUpManager.centerPopUp(tela); } [EventHandler("AppEvent.LOGIN_OK")] public function onLoginOk():void { appController.loadAllData();

} ]]> </fx:Script> <s:layout> <s:VerticalLayout paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"/> </s:layout> <fx:Declarations> <swiz:Swiz> <swiz:beanProviders> <beans:AppBean /> </swiz:beanProviders> <swiz:loggingTargets> <swiz:SwizTraceTarget id="myTraceTarget" /> </swiz:loggingTargets> <swiz:config> <swiz:SwizConfig eventPackages="events.*" viewPackages="views.*"/> </swiz:config> </swiz:Swiz> </fx:Declarations>

<s:controlBarContent> <fx:Array> <s:Label text="Consultorio Flex v0.1"/> <s:ButtonBar id="btnTelasPrincipais" dataProvider="{telas}" /> <s:Spacer width="100%"/> <mx:MenuBar itemClick="OnMenuItemClick(event)" labelField="@label" > <fx:XMLList> <menuitem label="Cadastros"> <menuitem label="Profissionais"/> <menuitem label="Materiais"/> <menuitem label="Convnios"/> <menuitem label="Especialidades"/> </menuitem> </fx:XMLList> </mx:MenuBar>

<s:Spacer width="25"/> </fx:Array> </s:controlBarContent> <!-- Aqui ficaro as telas principais. Outras telas sero popup. --> <mx:ViewStack id="telas" width="100%" height="100%"> <s:NavigatorContent id="telaInicio" label="Incio" width="100%" height="100%"> <views:Inicio width="100%" height="100%"/> </s:NavigatorContent> <s:NavigatorContent id="telaMarcarConsulta" label="Marcar Consulta"> </s:NavigatorContent> <s:NavigatorContent id="telaConsultas" label="Consultas"> </s:NavigatorContent> <s:NavigatorContent id="telaPacientes" label="Pacientes"> </s:NavigatorContent> </mx:ViewStack> </s:Application>

A aplicao principal injeta o controller AppController. No mtodo OnCreateComplete, abrimos a tela de login atravs do PopUpManager. A tela de login veremos logo a seguir. Logo abaixo do mtodo OnCreateComplete, temos o mtodo onLoginOk. O mtodo executado quando algum dispara o evento AppEvent.LOGIN_OK. Neste momento, usamos o appController para carregar todos os dados no mtodo loadAllData, que j vimos no AppController. O mtodo OnMenuItemClick executado quando clicamos em um item do menu direta do sistema. Aqui ficam algumas telas CRUD mais simples e por ela que executamos o cadastro de especialidades e o cadastro de profissionais. Veja que usamos um switch para descobrir qual item de menu foi clicado e atravs dele abrimos a relativa tela. A configurao do Swiz aponta para a classe AppBean.mxml, que exibida a seguir: /consultorioFlex/src/beans/AppBean.mxml
<?xml version="1.0" encoding="utf-8"?>

<swiz:BeanProvider xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:swiz="http://swiz.swizframework.org" xmlns:especialidadeservice="services.especialidadeservice.*" xmlns:profissionaisservice="services.profissionaisservice.*" xmlns:controllers="controllers.*" xmlns:appservice="services.appservice.*" > <fx:Script> <![CDATA[ import mx.controls.Alert; ]]> </fx:Script> <!-- essa a instncia que ser injetada no ControllerBase --> <swiz:ServiceHelper id="serviceHelper" /> <!-- Services --> <appservice:AppService id="appService"/> <especialidadeservice:EspecialidadeService id="especialidadeService" fault="Alert.show(event.fault.faultString)" /> <profissionaisservice:ProfissionaisService id="profissionaisService" fault="Alert.show(event.fault.faultString)" /> <!-- Controllers --> <controllers:AppController id="appController"/> <controllers:EspecialidadesController id="especialidadesController"/> <controllers:ProfissionaisController id="profissionaisController"/>

</swiz:BeanProvider>

At o momento a classe AppBeam possui referncias para os controllers e services de App, Especialidade e Profissional. Com o tempo iremos expandindo as referncias. Veja que, usamos uma conveno de nomes na criao destas instncias. Se a classe se chama EspecialidadesController, chamamos a varivel de especialidadesController, com a primeira letra em minsculo:
<controllers:EspecialidadesController id="especialidadesController"/>

Desta forma fazemos a injeo de dependncia usando o mesmo nome:


[Inject] [Bindable] public var especialidadesController:EspecialidadesController;

TELA DE LOGIN
A tela de login, apesar de ainda no fazer o login propriamente na tela, j chama o servidor atravs do appController. Veja que no existe um loginController, mesmo porque estamos centralizando algumas operaes no appController. /consultorioFlex/src/views/Login.mxml
<?xml version="1.0" encoding="utf-8"?> <s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="300" height="150" skinClass="skins.loginSkin" title="Login"> <fx:Declarations> <!-- Place non-visual elements (e.g., services, value objects) here --> </fx:Declarations> <fx:Script> <![CDATA[ import controllers.AppController; import mx.managers.PopUpManager; [Inject] public var appController:AppController; [EventHandler("AppEvent.LOGIN_OK")] public function onLoginOk():void { PopUpManager.removePopUp(this); } ]]> </fx:Script> <s:TextInput id="txtLogin" x="10" y="10" width="278" prompt="login"/>

<s:TextInput id="txtSenha" x="10" y="40" width="278" displayAsPassword="true" prompt="senha"/> <s:Button x="10" y="70" width="278" height="37" label="Ir"> <s:click> <![CDATA[ appController.doLogin(txtLogin.text,txtSenha.text); ]]> </s:click> </s:Button> </s:TitleWindow>

TELA CRUD DE ESPECIALIDADES


Nossa primeira tela CRUD envolve o cadastro de especialidades de um profissional. A aparncia da tela a seguinte:

A tela apresenta alguns elementos que sero padro no nosso sistema. Primeiro, toda tela CRUD modal ao sistema, e um TitleWindow. Possui uma barra de botes na parte inferior, com os botes Novo, Deletar e Salvar. O boto Salvar pode incluir ou atualizar um item. O cdigo exibido a seguir:

/consultorioFlex/src/views/Especialidades.mxml
<?xml version="1.0" encoding="utf-8"?> <s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="400" title="Especialidades" controlBarVisible="true" > <s:close> <![CDATA[ PopUpManager.removePopUp(this); ]]> </s:close> <fx:Declarations> <fx:Array id="formValidators"> <mx:StringValidator source="{txtNome}" property="text" trigger="{btnSave}" triggerEvent="click" /> </fx:Array> </fx:Declarations> <fx:Script> <![CDATA[ import controllers.EspecialidadesController; import import import import import mx.controls.Alert; mx.events.CloseEvent; mx.managers.PopUpManager; mx.validators.ValidationResult; mx.validators.Validator;

import spark.events.GridSelectionEvent; import consultorioFlex.EspecialidadeVO; [Inject] [Bindable] public var especialidadesController:EspecialidadesController; [EventHandler("EspecialidadesEvent.SAVE")] public function onSave():void { this.especialidadesController.getAll(); } [EventHandler("EspecialidadesEvent.DEL")] public function onDel():void { this.especialidadesController.getAll();

} [EventHandler("EspecialidadesEvent.CREATE")] public function onCreate():void { this.txtNome.setFocus(); } protected function onDataGridChange(event:GridSelectionEvent):void { this.especialidadesController.current = dataGrid.selectedItem as EspecialidadeVO; } protected function save():void { if (Validator.validateAll(formValidators).length==0) this.especialidadesController.save(); } protected function onAskDelete(event:CloseEvent):void { if (event.detail==Alert.YES) this.especialidadesController.del(); } ]]> </fx:Script> <s:DataGrid id="dataGrid" left="10" right="10" top="10" bottom="40" dataProvider="{especialidadesController.dataProvider}" selectionChange="onDataGridChange(event)" > <s:columns> <s:ArrayList> <s:GridColumn dataField="nome" headerText="Nome"></s:GridColumn> </s:ArrayList> </s:columns> </s:DataGrid> <s:TextInput id="txtNome" text="@{especialidadesController.current.nome}" enabled="{especialidadesController.current!=null}" bottom="10" left="10" right="10"> <s:enter> <![CDATA[ this.save(); ]]>

</s:enter> </s:TextInput> <s:controlBarContent> <fx:Array> <s:Button id="btnCreate" label="Novo" buttonMode="true" > <s:click> <![CDATA[ this.especialidadesController.create(); ]]> </s:click> </s:Button> <s:Button icon="@Embed('assets/icons/delete.png')" toolTip="Apagar" width="32" buttonMode="true" enabled="{especialidadesController.current!=null}" > <s:click> <![CDATA[ Alert.show("Deseja apagar?","Ateno",Alert.YES|Alert.NO,this,onAskDelete); ]]> </s:click> </s:Button> <s:Spacer width="100%"/> <s:Button id="btnSave" label="Salvar" buttonMode="true" toolTip="Salvar" enabled="{especialidadesController.current!=null}" > <s:click> <![CDATA[ this.save(); ]]> </s:click> </s:Button> </fx:Array> </s:controlBarContent> </s:TitleWindow>

Apesar da tela de especialidades ser relativamente simples, ela compe todos os recursos necessrios para uma tela CRUD. Por exemplo, os validadores do formulrio so criados em uma varivel chamada

formValidators. Vamos tentar manter este padro nas outras telas. Aps criar o validador do formulrio, abrimos a tag Script e injetamos o especialidadeController, que a classe responsvel na manipulao de dados. Depois temos diversos mtodos, que so responsveis em chamar o controller e executar as aes CRUD. Por exemplo, o mtodo save do formulrio chama o mtodo save do controller se a validao passar. Da mesma forma, o mtodo onAskDelete chama o mtodo Del do controller. Veja que o DataGrid da tela possui como dataProvider o valor: especialidadesController.dataProvider, que o array de dados criado no controller e que foi preenchido no mtodo loadAllData do AppControler. Veja que, o especialidadeController uma instncia que foi criada no AppBeam.mxml e que est disponvel globalmente. Ento, quando injetamos este controller no formulrio de especialidades, sabemos que o mesmo controller que est no Bean. E como o seu dataProvider j foi preenchido, aps logar no sistema, o DataGrid surgir com os dados assim que a tela de especialidades abrir, sem fazer uma requisio ao servidor. Esta uma das melhores facilidades que o Swiz lhe fornece. Voc cria uma instncia global de uma varivel no Bean, e usa essa instncia em toda a sua aplicao. Veremos mais vantagens dessa abordagem quando formos criar a tela de profissionais, pois nesta tela iremos criar um ComboBox de especialidades, e novamente usaremos a mesma instncia de especialidadeController.

TELA DE PROFISSIONAIS
A tela de profissionais um popup modal acionado pelo menu, e que contm mais campos que a tela de especialidades, alm de possuir um combobox para selecionar as especialidades do profissional. O desenho da tela visto a seguir. Criamos o datagrid com os nomes dos profissionais, seguido do seu respectivo formulrio.

Para chegar na fase de criao da tela, temos que percorrer diversos passos e construir cada bloco separadamente, para que no final possamos juntar tudo. Como estamos tratando de profissionais, a primeira tarefa criar a parte PHP que contm basicamente o ProfissionalVO e o ProfissionalService. Como eles j foram criados nos tpicos anteriores, certifique-se apenas que criou o servio ProfissionaisService no Flex. Certifique-se tambm que ProfissionaisService est no AppBeam, para que possa ser injetado em qualquer lugar da aplicao. Alm do ProfissionaisService no AppBeam, temos o controller ProfissionaisService.as, veja:

/consultorioFlex/src/controllers/ProfissionaisController.as
package controllers { import consultorioFlex.ProfissionalVO; import events.ProfissionaisEvent; import mx.collections.ArrayCollection; import mx.rpc.events.ResultEvent; import services.especialidadeservice.EspecialidadeService; import services.profissionaisservice.ProfissionaisService; public class ProfissionaisController extends ControllerBase { [Bindable] public var dataProvider:ArrayCollection; [Bindable] public var current:ProfissionalVO; [Inject] public var profissionaisService:ProfissionaisService; [Inject] public var especialidadeController:EspecialidadesController; public function getAll():void { this.serviceHelper.executeServiceCall(profissionaisService.getAll(),on GetAll); } public function onGetAll(event:ResultEvent):void { this.dataProvider = event.result as ArrayCollection; this.setEspecialidades(); dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.GETALL)); } public function save():void { this.serviceHelper.executeServiceCall(profissionaisService.save(curren t),onSave); } public function onSave(event:ResultEvent):void { this.current = new ProfissionalVO();

dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.SAVE)); } public function del():void { this.serviceHelper.executeServiceCall(profissionaisService.del(current .id),onDel); } public function onDel(event:ResultEvent):void { this.current = new ProfissionalVO;; this.dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.DEL)); } public function create():void { this.current = new ProfissionalVO; this.dispatcher.dispatchEvent(new ProfissionaisEvent(ProfissionaisEvent.CREATE)); } public function setEspecialidades():void { /* Neste momento, eu vou descobrir a especialidade de cada profisisonal. A princpio eu tenho somente o ID, mas vou chamar uma funo que ir me retornar o objeto especialidade. */ for each (var profissional:ProfissionalVO in this.dataProvider ) { profissional.especialidade = especialidadeController.getById(profissional.idEspecialidade); } } } }

Podemos agora criar a tela propriamente dita, que contm o seguinte cdigo: /consultorioFlex/src/views/Profissionais.mxml
<?xml version="1.0" encoding="utf-8"?> <s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="650" height="600"

title="Profissionais" controlBarVisible="true" xmlns:local="*" > <fx:Script> <![CDATA[ import consultorioFlex.ProfissionalVO; import controllers.EspecialidadesController; import controllers.ProfissionaisController; import events.ProfissionaisEvent; import import import import import mx.collections.ArrayCollection; mx.controls.Alert; mx.events.CloseEvent; mx.managers.PopUpManager; mx.validators.Validator;

import spark.events.GridSelectionEvent; [Inject] [Bindable] public var profissionaisController:ProfissionaisController; [Inject] [Bindable] public var especialidadesController:EspecialidadesController; protected function onAskDelete(event:CloseEvent):void { if (event.detail==Alert.YES) this.profissionaisController.del(); } protected function save():void { if (Validator.validateAll(formValidators).length==0) this.profissionaisController.save(); } [EventHandler("ProfissionaisEvent.CREATE")] public function onCreate():void { this.txtNome.setFocus(); } [EventHandler("ProfissionaisEvent.SAVE")] public function onSave():void { this.profissionaisController.getAll(); } [EventHandler("ProfissionaisEvent.DEL")] public function onDel():void

{ this.profissionaisController.getAll(); } protected function onDataGridChange(event:GridSelectionEvent):void { this.profissionaisController.current = dataGrid.selectedItem as ProfissionalVO; } ]]> </fx:Script> <s:close> <![CDATA[ PopUpManager.removePopUp(this); ]]> </s:close> <fx:Declarations> <fx:Array id="formValidators"> <mx:StringValidator source="{ddlEspecialidade}" property="selectedIndex" trigger="{btnSave}" triggerEvent="click" /> <mx:NumberValidator id="especialidadeValidator" source="{ddlEspecialidade}" property="selectedIndex" minValue="0" /> <mx:StringValidator source="{txtNome}" property="text" trigger="{btnSave}" triggerEvent="click" /> </fx:Array> </fx:Declarations> <s:controlBarContent> <fx:Array> <s:Button id="btnCreate" label="Novo" buttonMode="true" > <s:click> <![CDATA[ this.profissionaisController.create(); ]]> </s:click> </s:Button> <s:Button icon="@Embed('assets/icons/delete.png')" toolTip="Apagar" width="32" buttonMode="true"

enabled="{profissionaisController.current!=null}" > <s:click> <![CDATA[ Alert.show("Deseja apagar?","Ateno",Alert.YES|Alert.NO,null,onAskDelete); ]]> </s:click> </s:Button> <s:Spacer width="100%"/> <s:Button id="btnSave" label="Salvar" buttonMode="true" toolTip="Salvar" enabled="{profissionaisController.current!=null}" > <s:click> <![CDATA[ this.save(); ]]> </s:click> </s:Button> </fx:Array> </s:controlBarContent> <s:DataGrid id="dataGrid" left="10" top="10" bottom="10" width="200" dataProvider="{profissionaisController.dataProvider}" selectionChange="onDataGridChange(event)" > <s:columns> <s:ArrayList> <s:GridColumn dataField="nome" headerText="Nome"></s:GridColumn> </s:ArrayList> </s:columns> </s:DataGrid> <mx:TabNavigator right="10" top="10" bottom="10" width="420" creationPolicy="all" > <s:NavigatorContent width="100%" height="100%" label="Dados pessoais"> <s:Form id="form1" left="0" right="0" top="0" bottom="0" skinClass="spark.skins.spark.FormSkin"> <s:FormItem width="100%" label="Nome:"> <s:TextInput width="100%" text="@{profissionaisController.current.nome}" id="txtNome"/> </s:FormItem> <s:FormItem width="100%" label="Email:"> <s:TextInput width="100%" text="@{profissionaisController.current.email}"/> </s:FormItem> <s:FormItem width="100%" label="Especialidade:"> <s:HGroup width="100%"> <s:DropDownList id="ddlEspecialidade"

width="100%" dataProvider="{especialidadesController.dataProvider}" labelField="nome" selectedItem="@{profissionaisController.current.especialidade}" /> <s:Button id="btnEspecialidade" icon="@Embed('assets/icons/lupa.png')" width="30" buttonMode="true" > <s:click> <![CDATA[ var tela:TitleWindow; tela = new Especialidades(); PopUpManager.addPopUp(tela, this,true); PopUpManager.centerPopUp(tela); ]]> </s:click> </s:Button> </s:HGroup> </s:FormItem> <s:HGroup width="100%"> <s:FormItem width="100%" label="Login:"> <s:TextInput width="100%" text="@{profissionaisController.current.login}"/> </s:FormItem> <s:FormItem width="100%" label="Senha:"> <s:TextInput width="100%" text="@{profissionaisController.current.senha}" displayAsPassword="true"/> </s:FormItem> </s:HGroup> <s:HGroup width="100%"> <s:FormItem width="100%" label="Sexo:"> <local:ComboSexo id="ddSexo" width="100%" selectedItem="{profissionaisController.current.sexo}" > <local:change> <![CDATA[ profissionaisController.current.sexo = ddSexo.selectedItem.data; ]]> </local:change> </local:ComboSexo> </s:FormItem> <s:FormItem width="100%" label="Tel Residencial:"> <s:TextInput width="100%" text="@{profissionaisController.current.tel_residencial}"/> </s:FormItem> </s:HGroup>

<s:HGroup width="100%"> <s:FormItem width="100%" label="Tel Comercial:"> <s:TextInput width="100%" text="@{profissionaisController.current.tel_comercial}"/> </s:FormItem> <s:FormItem width="100%" label="Celular:"> <s:TextInput width="100%" text="@{profissionaisController.current.tel_celular}"/> </s:FormItem> </s:HGroup> </s:Form> </s:NavigatorContent> <s:NavigatorContent width="100%" height="100%" label="Endereo"> <s:Form x="0" y="0" width="100%" height="100%"> <s:FormItem width="100%" label="Logradouro:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_logradouro}"/> </s:FormItem> <s:HGroup width="100%"> <s:FormItem width="100%" label="Nmero:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_numero}"/> </s:FormItem> <s:FormItem width="100%" label="Complemento:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_complemento}" /> </s:FormItem> </s:HGroup> <s:HGroup width="100%"> <s:FormItem width="100%" label="Bairro:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_bairro}"/> </s:FormItem> <s:FormItem width="100%" label="Cidade:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_cidade}"/> </s:FormItem> </s:HGroup> <s:HGroup width="100%"> <s:FormItem width="100%" label="Estado:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_estado}" /> </s:FormItem> <s:FormItem width="100%" label="CEP:"> <s:TextInput width="100%" text="@{profissionaisController.current.end_cep}"/> </s:FormItem> </s:HGroup> </s:Form> </s:NavigatorContent> </mx:TabNavigator>

</s:TitleWindow>

O cdigo desta tela realmente extenso, mas segue os padres da tela de especialidades, exceto pelo combobox de especialidades, que tem um boto que abre a tela de especialidades. Veja que quando abrimos a tela de especialidades dentro da tela de profissionais podemos editar os dados da tela de especialidades, como por exemplo adicionar um novo registro, e estes dados so atualizados automaticamente na tela de profissionais. Isso acontece porque estamos usando Swiz e a fonte de dados de especialidades a mesma para toda a aplicao. A nica funcionalidade que faltou seria clicar duas vezes no grid de especialidades e selecionar automaticamente a especialidade do profissional. Veremos isso logo a seguir.

CRIANDO UM LOOKUP DE ESPECIALIDADES


Um lookup um conceito, ou controle, muito usado em sistemas reais, que tem a funcionalidade de buscar um dado para uma determinada tela. No caso da tela de profissionais, no momento em que estamos preenchendo a especialidade, suponha que queremos cadastrar uma nova especialidade. Para facilitar o trabalho do usurio, ao invs de termos que fechar a tela de profissionais e abrir a tela de especialidades, criamos um boto ao lado do combobox que ir abrir a tela de especialidades:
<s:Button id="btnEspecialidade" icon="@Embed('assets/icons/lupa.png')" width="30" buttonMode="true" > <s:click> <![CDATA[ var tela:TitleWindow; tela = new Especialidades(); PopUpManager.addPopUp(tela, this,true); PopUpManager.centerPopUp(tela); ]]> </s:click> </s:Button>

Quando abrimos o nosso popup de especialidades, o usurio pode criar uma nova especialidade e fechar a janela. Ento o combobox de especialidades atualizado automaticamente e o usurio pode selecionar a nova especialidade recm criada. Ainda existem outros tipos de lookup que veremos nas prximas telas, sendo que a principal dela uma caixa de texto com o boto lookup, onde o usurio seleciona um registro e este retorna para a tela anterior.

S-ar putea să vă placă și