Framework MIOLO 2.5 Tutorial: Criando um módulo Versão 2011.1.oracle CGCO/UFJF
Framework MIOLO 2.5 Tutorial: Criando um módulo 1. Introdução O processo de desenvolvimento de aplicações com o MIOLO possui as seguintes etapas (<miolo> indica o diretório de instalação do Miolo): - Modelagem das classes e do banco de dados; - Criação da estrutura do módulo, com seus subdiretórios; - Definição do tema a ser utilizado (um já existente, ou a criação de um novo tema); - Criação de controles específicos para o módulo, caso seja necessário; - Criação do arquivo de configuração do módulo em <miolo>/modules/<modulo>/etc/module.conf; - Criação da classe handler em <miolo>/modules/<modulo>/handler/handler.class; - Criação do handler principal em <miolo>/modules/<modulo>/handler/main.inc; - Criação das classes de negócio em <miolo>/modules/<modulo>/classes caso existam; - Criação dos formulários em <miolo>/modules/<modulo>/forms caso existam; O objetivo deste texto é examinar cada uma destas etapas, de forma simplificada, servindo como um pequeno tutorial para criação de uma aplicação simples com o MIOLO. 2. Estudo de caso Para este tutorial estaremos adotando um modelo simples, que poderá ser expandido em futuros tutoriais. Trata-se de uma instituição de ensino que oferece vários cursos. É feito um cadastro dos dados pessoais dos alunos e a matrícula dos mesmos em um determinado curso. Para simplificar, cada aluno pode estar matriculado em apenas um curso. Um curso pode ter vários professores, e um professor pode estar associado a vários cursos. Um curso pertence a uma certa área e é ministrado em uma sala específica. 3. Modelagem das classes e do banco de dados As classes e o banco de dados usados nesta versão do tutorial foram modelados com a ferramenta Visual Paradigm.
4. Estrutura do módulo A estrutura do módulo pode ser criada através do módulo Wizard. Para este tutorial, você deverá criar o módulo curso. O módulo deverá conter os seguintes subdiretórios: - classes - classes/map - forms - grids - handlers - etc - html - html/images - sql 5. Classe handler O objetivo desta classe é incluir o código do handler específico refrenciado na URL. Seu código inicial é criado pelo Wizard. <miolo>/modules/curso/handlers/handler.class 6. Handler principal O objetivo deste handler é apresentar um panel para acesso aos objetos do sistema. O esqueleto deste handler é criado pelo Wizard e deve ser completado pelo desenvolvedor. <miolo>/modules/exemplo/handlers/main.inc 7. Criação das classes de negócio e mapeamento para persistência As classes/mapeamentos são criadas no diretório <miolo>/modules/curso/classes através do Wizard. O módulo wizard utiliza o arquivo XMI exportado a partir do VP para criar o esqueleto das classes de negócio e o mapeamento XML correspondente. 8. Arquivo de configuração do módulo O arquivo configuração do módulo (inicialmente vazio) é criado pelo Wizard e está localizado em: <miolo>/modules/curso/etc/module.conf
A configuração para este tutorial será: <?xml version="1.0" encoding="iso-8859-1" standalone="yes" <conf> <db> <curso> <system>oracle8</system> <host>hera</host> <name>hera.cpd.ufjf.br/ufjf</name> <user>cursomiolo</user> <password>curso</password> <jdbc_driver>oracle.jdbc.driver.oracledriver</jdbc_driver> <jdbc_db>jdbc:oracle:thin:@hera.cpd.ufjf.br:1521:ufjf</jdbc_db> </curso> </db> <options> <startup>curso</startup> </options> <login> <check>0</check> </login> </conf> 9. Formulários CRUD Formulários CRUD (Create, Retrieve, Update, Delete) são usados para fazer manutenção e consultas nos dados referentes a uma classe de negócio, que estão armazenados no banco de dados. A estrutura básica para utilização de formulários CRUD é criada pelo Wizard, e deve ser customizada pelo desenvolvedor para cada classe específica. Cada classe possui um handler referente ao formulário CRUD. Este handler apenas executa o formulário base da classe. Através deste formulário base é que são acessados os métodos da classe (que atuam sobre todas as instâncias) e os métodos que atuam sobre uma instância específica, após esta ter sido selecionada. Esta estrutura busca refletir a modelagem das classes, uma vez que além dos métodos CRUD, outros métodos da classe podem ser implementados a partir do formulário base.
Exemplos para a classe CURSO
- Handler curso.inc Executado a partir do panel principal do modulo. URL: http://miolo.ufjf.br:8xxx/index.php?module=curso&action=curso <?php // handler: module=curso action=<classe>:<comando> $theme->clearcontent(); $ui = $MIOLO->getUI(); // $action = <comando> a ser executado; assume find se nenhum objeto estiver // sendo editado ou main se um objeto foi selecionado $action = $context->shiftaction(); // instancia o formulário base $form = $ui->getform('curso','frmcursobase',$action,'curso'); $theme->setcontent($form); - Formulário Base frmcursobase.class Executado a partir do handler curso.inc. Localização: modules/curso/forms/curso/frmcursobase.class <?php class frmcursobase extends MActionPanelObject { // $action = <comando> a ser executado, passado na chamada em curso.inc function construct($action) { // $item contem o OID caso algum objeto tenha sido selecionado global $MIOLO, $item; // $close: para onde retornar quando o formulário base é fechado $close = $MIOLO->getActionURL('curso','main'); // $icon: ícone do formulário, caso exista $icon = $MIOLO->getUI()->getImage('curso','curso.png'); // instancia o panel para a classe parent:: construct('panel','curso', $close, $icon); // este formulário está em que modulo? $this->setbasemodule('curso'); // qual action para chegar neste formulário? $this->setbaseaction('curso'); // qual o diretório base para os formulários (em modules/<module>/forms/)? $this->setbaseformfolder('curso'); // instancia um objeto específico (OID = $item), ou apenas a classe $this->setbasebusiness('curso','curso', $item); // comandos relativos a classe (e não a uma instância específica) // $this->addcommand(<comando>,<formulário>,<título>); $this->addcommand('main','frmcurso',''); $this->addcommand('find','frmcursofind','pesquisar'); $this->addcommand('new','frmcursonew','inserir Novo'); // comandos relativos a uma instância específica // tratados pelo form principal (mcompound) $this->addcommand('data','frmcurso',''); $this->addcommand('del','frmcurso',''); // adiciona na tela o formulário relativo ao comando recebido $this->addform($action); /** * Retorna um MDataObject usado como parâmetro para as views da classe. * Deve ser customizado para cada classe.
*/ public function getparams() { $params = new MDataObject(); // obtem um array de Areas $area = $this->manager->getbusiness('curso','area'); $params->areas = $area->listall()->chunkresult(); return $params; A figura a seguir apresenta o formulário base da classe Curso, com as ações Pesquisar e Inserir Novo, e o formulário de pesquisa (frmcursofind), que é incluído por default. - Formulário de Pesquisa frmcursofind.class Executado a partir do formulário base (frmcursobase), permite a busca e seleção (retrieve) de um objeto específico. Localização: modules/curso/forms/curso/frmcursofind.class <?php class frmcursofind extends MFormObject { public function createfields() { $filter = MUtil::NVL(MForm::getFormValue('nome'), '%'); $params = $this->base->getparams(); $params->query = $this->object->listall(strtoupper($filter), 'nome'); $params->href = $this->manager->getcurrenturl(); $params->id = $this->object->getoidname(); $params->link = $this->manager->getactionurl($this->module, $this->action, '#0#'); $this->getview($params); $this->defaultbutton = false; - View XML frmcurso.xml Definição dos campos (controles) do formulário em um arquivo XML. O Wizard cria uma view básica, que deve ser customizada pelo desenvolvedor. O nome do arquivo da view corresponde, de forma geral, ao nome da classe do formulário correspondente (com exceção de frm<classe>.xml, que é usado pelos formulários de inserção e edição de dados). A view pode acessar os parâmetros passados via código do formulário, através da variável $params. Localização: modules/curso/views/curso/frmcurso.xml <?xml version="1.0" encoding="iso-8859-1" <!DOCTYPE mfd SYSTEM "../../etc/view.dtd"> <mfd>
<fields> <mhiddenfield id="idcurso" /> <mtextfield id="nome" label="nome do Curso" size="20" /> <mselection id="idarea" label="área" options="${$params->areas" /> </fields> <buttons> <mbutton id="btnpost" text="enviar" /> </buttons> </mfd> - View XML frmcursofind.xml <?xml version="1.0" encoding="iso-8859-1" <!DOCTYPE mfd SYSTEM "../../etc/view.dtd"> <mfd> <fields> <mhcontainer> <mtextfield id="nome" label="nome do Curso" size="30" /> <mbuttonfind/> </mhcontainer> <mdatagrid id="cursofind" query="${$params->query" href="${$params->href" pagelength="15" index="0" > <mdatagridcolumn field="${$params->id" title="${$params->id" align="right" width="0%" visible="${false" /> <mdatagridhyperlink field="nome" title="nome do Curso" href="${$params- >link" width="100%" visible="${true" /> </mdatagrid> </fields> </mfd> - Formulário de Inserção frmcursonew.class Executado a partir do formulário base (frmcursobase), permite a inserção a criação de um novo objeto. Localização: modules/curso/forms/curso/frmcursonew.class frmcursonew estende da classe MFormObject, que tem como principais propriedades e métodos: $this->base : formulário base da classe (ex. frmcursobase) $this->module : nome do modulo corrente $this->action : campo action da URL corrente (usado para construção de outras URL) $this->object : instância do objeto selecionado (caso exista) $this->formfolder : nome do folder onde estão os formulários (definido em frmcursobase) $this->command : nome do comando corrente (sendo executado) public function getformname($command = '') - Obtém o nome do formulário associado a um comando. public function geturl($command = '', $oid = '', $args = '') - Constrói a URL relativa a um comando public function go($command = '', $oid = '') - Executa um redirect para a URL relativa a um comando <?php class frmcursonew extends MFormObject { public function createfields() { $params = $this->base->getparams(); $this->setfieldsfromxml($this->module, $this->getformname('main'), $params, $this->formfolder); public function btnpost_click() { $data = $this->getdata(); $this->object->setdata($data); try {
$this->object->save(); $this->go('main', $this->object->getoidvalue()); catch (Exception $e) { $this->adderror($e->getmessage()); - Formulário Principal frmcurso.class Executado a partir do formulário base (frmcursobase). É um MFormCompound, usado para acessar os dados e métodos de uma instância específica, previamente selecionada (cujo OID é passado na variável $item). Localização: modules/curso/forms/curso/frmcurso.class Um MFormCompound é um tipo de formulário que permite a composição de vários controles (labels, panels e forms), conforme a figura abaixo. array_info: usado para apresentar dados do objeto sendo exibido; array_panel: panel de ações para acessar os métodos relativos ao objeto sendo exibido; array_form: formulário(s) referente(s) à ações acessíveis via panel. Um MFormCompound tem como propriedades importantes: $this->base : formulário base da classe (ex. frmcursobase) $this->module : nome do modulo corrente $this->object : instância do objeto selecionado $this->oid : OID da instância do objeto selecionado
<?php class frmcurso extends MCompoundForm { public function construct($base) { $this->base = $base; parent:: construct(); $this->settitle($this->object->nome); public function createfields() { // info: atributos do objeto $this->addinformation(new MTextLabel('txtnome', $this->object->nome, 'Nome do Curso')); // panel: associa cada comando ao formulario correspondente $this->addpanel('panel'); $this->addpanelaction('panel', "data", 'frmcursodata', 'Editar', $this->getui()- >getimage($this->module, 'edit.png')); $this->addpanelaction('panel', "del", 'frmcursodel', 'Excluir', $this->getui()- >getimage($this->module, 'del.png')); // form: inclui o formulario correspondente ao comando recebido $this->addform(); - Formulário de Edição frmcursodata.class Executado a partir do formulário principal (frmcurso), permite a edição dos dados de um objeto previamente selecionado. Localização: modules/curso/forms/curso/frmcursodata.class frmcursodata também estende da classe MFormObject, cujas principais propriedades e métodos foram apresentadas anteriormente.
<?php class frmcursodata extends MFormObject { public function construct($base) { parent:: construct($base, 'Dados'); public function createfields() { $params = $this->base->getparams(); $this->setfieldsfromxml($this->module, $this->getformname('main'), $params, $this->formfolder); $this->setdata($this->object); public function btnpost_click() { $this->object->setdata($this->getdata()); $txt = "[ Nome do Curso: {$this->object->nome ] "; $txtalt = $txt. "alterado com sucesso."; $action_ok = $this->geturl('find'); try { $this->object->save(); $fields[] = MPrompt::Information($txtAlt, $action_ok); $this->setfields($fields); $buttons = array(); $this->setbuttons($buttons); catch (Exception $e) { $this->adderror($e->getmessage()); - Formulário de Exclusão frmcursodel.class Executado a partir do formulário principal (frmcurso), permite a exclusão do banco de dados de um objeto previamente selecionado. Localização: modules/curso/forms/curso/frmcursodel.class <?php class frmcursodel extends MFormObject { public function createfields() { $id = $this->object->getoidvalue(); $txt = "[ Nome do Curso: {$this->object->nome ] "; $txtexc = $txt. "excluído com sucesso"; $txtconf = "Tem certeza que deseja excluir ". $txt. "?"; $action_sim = $this->geturl('del', $id, array('conf' => 'sim')); $action_nao = $this->geturl('main', $id); $action_ok = $this->geturl('find'); $conf = MForm::getFormValue('conf'); if ($conf == 'sim') { try { $this->object->delete(); $fields[] = MPrompt::Information($txtExc, $action_ok); catch (Exception $e) {
$fields[] = MPrompt::Error($e->getMessage, $action_nao); else { $fields[] = MPrompt::Question($txtConf, $action_sim, $action_nao); $this->setfields($fields); $this->defaultbutton = false; 10. Lookup O lookup é uma janela de pesquisa que permite selecionar dados para preenchimento de um campo ou formulário. É composto por 4 elementos principais: a) handler lookup.inc b) classe business lookup.class c) método de pesquisa dentro da classe lookup.class d) controle visual no formulário a) handler lookup.inc O handler lookup.inc é responsável por tratar todas as solicitações para exibição de um lookup. Em uma aplicação composta por muitos módulos, geralmente o lookup.inc é colocado no módulo principal ou no módulo common da aplicação. Sua estrutura padrão é a seguinte: <?php // instancia o controle Lookup $lookup = new MLookup(); // importa a classe de negócio padrão lookup.class $ok = $MIOLO->Uses('/classes/lookup.class',$lookup->module); $MIOLO->Assert($ok,_M('Arquivo modules/@1/classes/lookup.class não encontrado.<br>'. 'Este arquivo deve implementar a classe Business@1Lookup '. 'contendo a função Lookup@2.', 'miolo',$lookup->module, $lookup->item)); // atualiza a configuração do modulo usado $MIOLO->conf->LoadConf($lookup->module); $page->addscript('m_lookup.js'); $page->settitle('janela de Pesquisa'); // instancia a classe de negócio $businessclass = "Business{$lookup->moduleLookup"; $object = new $businessclass(); // executa o método de pesquisa $lookupmethod = "Lookup{$lookup->item"; $object->$lookupmethod($lookup); // exibe o grid de lookup $page->setaction($lookup->href); $theme->setcontent($lookup->grid); O módulo que contém o handler lookup.inc é chamado basemodule. b) Classe business lookup.class A classe Business<Modulo>Lookup, definida no arquivo lookup.class, contém os métodos de lookup de cada modulo. Sua estrutura padrão é a seguinte: <?php class BusinessCursoLookup { function lookupaaa($lookup) { function lookupbbb($lookup) {
function lookupccc($lookup) { c) Métodos de pesquisa em lookup.class Os métodos de lookup de cada modulo são definidos dentro da classe Business<Modulo>Lookup. O objetivo dos métodos de lookup é definir: os campos usados para fazer a pesquisa, a pesquisa propriamente dita (com uma query ou um criteria) e o grid para exibição dos resultados. Sua estrutura padrão é a seguinte: function lookupcurso($lookup) { // obtem o valor do filtro (passado via formulario ou via janela de pesquisa) $filter = $lookup->getfiltervalue('filter'); if (!$filter) { $filter = $lookup->getfiltervalue(); // adiciona o campo do filtro na janela de pesquisa $lookup->addfilterfield( new MTextField('filter', $filter,'curso', 40)); // define as colunas do grid de resultados $columns = array( new MDataGridColumn('idCurso','ID','left', true, '0%',false), new MDataGridColumn('nome','Nome','left', true, '100%',true), ); // faz a consulta ao banco (via MSQL, MQuery, ou criteria) $curso = $this->getbusiness('curso','curso'); $query = $curso->listall('nome',$filter,'nome'); // exibe o grid (de acordo com o tipo de consulta feita) $lookup->setquerygrid($query, $columns, 'Pesquisa Curso', 15, 0); d) controle visual no formulário Para inclusão de um lookup no formulário podem ser usados 3 controles, dependendo da situação: - MLookupField: pode usar mais de um campo como filtro - MLookupTextfield: usa um campo MTextField como filtro - MLookupFieldValue: não permite que o usuário digite um filtro Cada controle visual tem os seguintes atributos: - basemodule: modulo onde está o handler lookup.inc - module: modulo onde está o arquivo lookup.class, com os métodos de pesquisa - item: indica o método da classe lookup.class a ser usado com o controle. O nome do método é Lookup<Item> - event: como a seleção deve ser tratada: - filler: preenche os campos indicados em 'related' com a resposta - method : chama um método específico - related : junto com event=filler indica quais campos serão preenchidos. A ordem dos campos DEVE SER igual à ordem das colunas no grid de lookup (use "none" quando não houver no formulário um campo correspondente a coluna do grid) - filter : quais campos cujos valores serão usados como filtros para o lookup <mlookuptextfield id="curso" label="curso" size="30" basemodule="curso" module="curso" item="curso" event="filler" related="idcurso,curso" filter="curso"/>
11. WebFlow A implementação MIOLO WebFlows está baseada na definição de um Diagrama de Atividades (UML2). O diagrama possui 3 elementos básicos: Ações (action), Fluxos (flow) e Decisões (decision). É necessário definir uma ação inicial (initialaction) e uma ação final (finalaction). A ação initial está associada a um fluxo inicial. A ação final está associada a um fluxo final (que encerra o webflow) e é representada por uma URL a ser executada no final do webflow. Cada ação é associada a um formulário da classe MFormFlow. Os dados do webflow estão armazenados na sessão, mas tem o escopo do webflow (ou seja, eles são removidos da sessão ao fim do webflow). Os dados estão armazenados em um objeto de valor (MDataObject). Estes dados podem ser acessados dentro dos formulários através dos métodos $this->getflowdata('nome_do_atributo') e $this->setflowdata ('nome_do_atributo','valor') - se o 'nome_do_atributo' não for informado, é retornado todo o objeto flowdata. Cada ação pode ter vários fluxos associados. Cada fluxo tem um nome e indica qual a ação seguinte será executada. As decisões são associadas a uma ação. Elas podem ser codificadas dentro dos formulários, indicando qual o fluxo seguinte a ser executado. Também podem ser codificadas como métodos no panel do webflow, sendo executado imediatamente após a execução da ação. Para cada ação podem ser executados métodos antes da execução da ação (onentry) e depois da execução da ação (onexit). O método onexit é executado após o método relativo à decisão (se houver algum). Toda tentativa de acessar uma ação sem que a ação inicial tenha sido executada redireciona o flow para a ação inicial, para evitar inconsistências no flowdata. Para definir a ação seguinte a ser executada, os formulários podem executar os seguintes métodos: - $this->geturl('nome_do_fluxo ou ação') : obtem a URL para execução da ação associada ao fluxo indicado - $this->go('nome_do_fluxo ou acao') : executa um redirect para a ação indicada ou para o fluxo associado a ação atual - $this->restarturl() : obtem a URL para a ação inicial - $this->restart() : volta para o ação inicial (os dados do webflow são reiniciados) - $this->finalizeurl() :obtem a URL para a ação final - $this->finalize(): executa a ação final (encerra o webflow e apaga o flowdata). - $this->previousurl(): obtem a URL para ação executada imediatamente antes da ação atual. - $this->nexturl(): obtem a URL para ação seguinte à ação atual (ação default - associada ao primeiro fluxo associado à ação). Neste tutorial será implementado um webflow simples, apenas para mostrar as principais funcionalidades. O webflow será implementado como uma ação da classe Curso.