Giihh 6 Denunciar post Postado Julho 19, 2014 Meu conselho é! Modele suas suas tabelas EER com Mysql Workbench "é de Grátis" e do tipo InnoDB, evite relacionar a tabela inserindo id_empresa manualmente em um sistema SaaS que é seu caso. tipo id_empresa tipo int e só Tank´s pelas dicas Williams, fiz o diagrama abaixo e importei no MySQL (phpMyAdmin) Duvidas: O relacionamento e os tipos de dados estou fazendo certo? Estou criando as classes (utilizando o modelo MVC) ai na pasta MODEL fazendo as funções de CRUD, para cada uma das tabelas acima. Só que na prática não estou conseguindo ver como colocar nas funções, alterações no BD de uma determinada sessão, por exemplo a EmpresaFulana acessou o sistema e possui o nivel 2 de acesso (tabela Usuários).... ai tudo que esse usuário alterar será salvo no id da EmpresaFulana. Compartilhar este post Link para o post Compartilhar em outros sites
Ricardo Saraiva 84 Denunciar post Postado Julho 20, 2014 Para pode usar CPF char(11) e neste caso armazena-se somente os números, prefiro trabalhar com uma tabela separada para telefone pois futuramente pode surgir a necessidade de armazenar mais de um telefone por empresa, usuário, etc.. A estrutura de uma tabela para telefone uso algo mais ou menos assim. id_telefone - INT(11) referencia - VARHCAR(30) nome da tabela referencia de onde é o telefone (empresa.usuário) id_referencia - INT(11) id da referencia do telefone (empresa,usuário) telefone - VARCHAR(11) armazeno telefones no formato (00) 0000-0000 ou (00) 00000-0000 somente os numeros tipo - ENUM(movel,fixo) principal - TINYINT(1) usado para definir se é telefone principal (empresa, usuário) Normalização de dados http://www.luis.blog.br/normalizacao-de-dados-e-as-formas-normais.aspx Compartilhar este post Link para o post Compartilhar em outros sites
Giihh 6 Denunciar post Postado Julho 23, 2014 Usando o Framework Yii, para a tabela "empresa" gerei uma classe Empresa do model e depois o crud da tabela empresa...usando Gii, Ai automaticamente é criado o arquivo Empresa.php na pasta MODEL, que contem a classe Empresa. Quando gero o Crud no Gii com a classe Empresa do model, o Gii cria na pasta Controller o arquivo EmpresaController.php Já na Pasta View ele cria... os seguintes diretórios: _form.php, _search.php, _view.php, admin.php, create.php, index.php, update.php e view.php Todas as ações do crud da tabela empresa estão funcionando perfeitamente. Na pasta model: classe Empresa <?php /** * This is the model class for table "empresa". * * The followings are the available columns in table 'empresa': * @property integer $id * @property string $empresa * * The followings are the available model relations: * @property Notebooks[] $notebookses * @property Celulares[] $ccelularees * @property Funcionarios[] $funcionarios * @property Unidades[] $unidadeses * @property Usuarios[] $usuarioses */ class Empresa extends CActiveRecord { /** * @return string the associated database table name */ public function tableName() { return 'empresa'; } /** * @return array validation rules for model attributes. */ public function rules() { // NOTE: you should only define rules for those attributes that // will receive user inputs. return array( array('empresa', 'required'), array('empresa', 'length', 'max'=>100), // The following rule is used by search(). // @todo Please remove those attributes that should not be searched. array('id, empresa', 'safe', 'on'=>'search'), ); } /** * @return array relational rules. */ public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'notebookses' => array(self::HAS_MANY, 'Notebooks', 'empresa_id'), 'celulareses' => array(self::HAS_MANY, 'Celulares', 'empresa_id'), 'funcionarios' => array(self::HAS_MANY, 'Funcionarios', 'empresa_id'), 'unidadeses' => array(self::HAS_MANY, 'Unidades', 'empresa_id'), 'usuarioses' => array(self::HAS_MANY, 'Usuarios', 'empresa_id'), ); } /** * @return array customized attribute labels (name=>label) */ public function attributeLabels() { return array( 'id' => 'ID', 'empresa' => 'Empresa', ); } /** * Retrieves a list of models based on the current search/filter conditions. * * Typical usecase: * - Initialize the model fields with values from filter form. * - Execute this method to get CActiveDataProvider instance which will filter * models according to data in model fields. * - Pass data provider to CGridView, CListView or any similar widget. * * @return CActiveDataProvider the data provider that can return the models * based on the search/filter conditions. */ public function search() { // @todo Please modify the following code to remove attributes that should not be searched. $criteria=new CDbCriteria; $criteria->compare('id',$this->id); $criteria->compare('empresa',$this->empresa,true); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, )); } /** * Returns the static model of the specified AR class. * Please note that you should have this exact method in all your CActiveRecord descendants! * @param string $className active record class name. * @return Empresa the static model class */ public static function model($className=__CLASS__) { return parent::model($className); } } Acima no public function relations() ele identificou as outra tabelas que tem possuem a chave Id Empresa. Na pasta Controller: Arquivo EmpresaController.php. <?php class EmpresaController extends Controller { /** * @var string the default layout for the views. Defaults to '//layouts/column2', meaning * using two-column layout. See 'protected/views/layouts/column2.php'. */ public $layout='//layouts/column2'; /** * @return array action filters */ public function filters() { return array( 'accessControl', // perform access control for CRUD operations 'postOnly + delete', // we only allow deletion via POST request ); } /** * Specifies the access control rules. * This method is used by the 'accessControl' filter. * @return array access control rules */ public function accessRules() { return array( array('allow', // allow all users to perform 'index' and 'view' actions 'actions'=>array('index','view'), 'users'=>array('*'), ), array('allow', // allow authenticated user to perform 'create' and 'update' actions 'actions'=>array('create','update'), 'users'=>array('@'), ), array('allow', // allow admin user to perform 'admin' and 'delete' actions 'actions'=>array('admin','delete'), 'users'=>array('admin'), ), array('deny', // deny all users 'users'=>array('*'), ), ); } /** * Displays a particular model. * @param integer $id the ID of the model to be displayed */ public function actionView($id) { $this->render('view',array( 'model'=>$this->loadModel($id), )); } /** * Creates a new model. * If creation is successful, the browser will be redirected to the 'view' page. */ public function actionCreate() { $model=new Empresa; // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Empresa'])) { $model->attributes=$_POST['Empresa']; if($model->save()) $this->redirect(array('view','id'=>$model->id)); } $this->render('create',array( 'model'=>$model, )); } /** * Updates a particular model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id the ID of the model to be updated */ public function actionUpdate($id) { $model=$this->loadModel($id); // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Empresa'])) { $model->attributes=$_POST['Empresa']; if($model->save()) $this->redirect(array('view','id'=>$model->id)); } $this->render('update',array( 'model'=>$model, )); } /** * Deletes a particular model. * If deletion is successful, the browser will be redirected to the 'admin' page. * @param integer $id the ID of the model to be deleted */ public function actionDelete($id) { $this->loadModel($id)->delete(); // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser if(!isset($_GET['ajax'])) $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin')); } /** * Lists all models. */ public function actionIndex() { $dataProvider=new CActiveDataProvider('Empresa'); $this->render('index',array( 'dataProvider'=>$dataProvider, )); } /** * Manages all models. */ public function actionAdmin() { $model=new Empresa('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['Empresa'])) $model->attributes=$_GET['Empresa']; $this->render('admin',array( 'model'=>$model, )); } /** * Returns the data model based on the primary key given in the GET variable. * If the data model is not found, an HTTP exception will be raised. * @param integer $id the ID of the model to be loaded * @return Empresa the loaded model * @throws CHttpException */ public function loadModel($id) { $model=Empresa::model()->findByPk($id); if($model===null) throw new CHttpException(404,'The requested page does not exist.'); return $model; } /** * Performs the AJAX validation. * @param Empresa $model the model to be validated */ protected function performAjaxValidation($model) { if(isset($_POST['ajax']) && $_POST['ajax']==='empresa-form') { echo CActiveForm::validate($model); Yii::app()->end(); } } } A TABELA EMPRESA FOI CRIADO O MODEL E O CRUD, E AS OUTRAS TABELAS? O Gii esta criando o model para as demais tabelas mas não esta criando o CRUD... aparece a seguinte mensagem de erro. Table 'usuarios' has a composite primary key which is not supported by crud generator. Pesquisei e é um erro comum quando uma tabela tem mais de uma chave primária... no caso, a tabela usuarios e demais tem relacionamento com id_empresa. Então só gera mesmo os demais arquivos e classes do model. Por não gerar o CRUD automaticamente, não são criadas na pasta do Controller e as views de cada crud. Exemplo do model dos demais arquivos. Código da classe Usuarios (arquivo Usuarios.php da pasta Model) <?php /** * This is the model class for table "usuarios". * * The followings are the available columns in table 'usuarios': * @property integer $id * @property string $nome * @property string $login * @property string $senha * @property string $nascimento * @property string $cpf * @property string $cnpj * @property string $end * @property string $tel * @property string $cel * @property string $nivel * @property integer $empresa_id * * The followings are the available model relations: * @property Empresa $empresa */ class Usuarios extends CActiveRecord { /** * @return string the associated database table name */ public function tableName() { return 'usuarios'; } /** * @return array validation rules for model attributes. */ public function rules() { // NOTE: you should only define rules for those attributes that // will receive user inputs. return array( array('nome, login, senha, nascimento, cpf, cnpj, end, tel, cel, nivel, empresa_id', 'required'), array('empresa_id', 'numerical', 'integerOnly'=>true), array('nome, end', 'length', 'max'=>200), array('login, senha', 'length', 'max'=>100), array('cpf, cnpj, tel, cel', 'length', 'max'=>50), array('nivel', 'length', 'max'=>1), // The following rule is used by search(). // @todo Please remove those attributes that should not be searched. array('id, nome, login, senha, nascimento, cpf, cnpj, end, tel, cel, nivel, empresa_id', 'safe', 'on'=>'search'), ); } /** * @return array relational rules. */ public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'empresa' => array(self::BELONGS_TO, 'Empresa', 'empresa_id'), ); } /** * @return array customized attribute labels (name=>label) */ public function attributeLabels() { return array( 'id' => 'ID', 'nome' => 'Nome', 'login' => 'Login', 'senha' => 'Senha', 'nascimento' => 'Nascimento', 'cpf' => 'Cpf', 'cnpj' => 'Cnpj', 'end' => 'End', 'tel' => 'Tel', 'cel' => 'Cel', 'nivel' => 'Nivel', 'empresa_id' => 'Empresa', ); } /** * Retrieves a list of models based on the current search/filter conditions. * * Typical usecase: * - Initialize the model fields with values from filter form. * - Execute this method to get CActiveDataProvider instance which will filter * models according to data in model fields. * - Pass data provider to CGridView, CListView or any similar widget. * * @return CActiveDataProvider the data provider that can return the models * based on the search/filter conditions. */ public function search() { // @todo Please modify the following code to remove attributes that should not be searched. $criteria=new CDbCriteria; $criteria->compare('id',$this->id); $criteria->compare('nome',$this->nome,true); $criteria->compare('login',$this->login,true); $criteria->compare('senha',$this->senha,true); $criteria->compare('nascimento',$this->nascimento,true); $criteria->compare('cpf',$this->cpf,true); $criteria->compare('cnpj',$this->cnpj,true); $criteria->compare('end',$this->end,true); $criteria->compare('tel',$this->tel,true); $criteria->compare('cel',$this->cel,true); $criteria->compare('nivel',$this->nivel,true); $criteria->compare('empresa_id',$this->empresa_id); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, )); } /** * Returns the static model of the specified AR class. * Please note that you should have this exact method in all your CActiveRecord descendants! * @param string $className active record class name. * @return Usuarios the static model class */ public static function model($className=__CLASS__) { return parent::model($className); } } DUVIDA No código da classe Empresa (1° código acima) foi identificado que a tabela empresa tem relacionamento com outras tabelas. O Gii gera o arquivo e classe Usuarios no model para a Tabela usuários (posso fazer o mesmo com as demais tabelas)... mas não gera o CRUD, aparece o erro. No exemplo da classe Usuarios, o public function relations() indica o relacionamento com a tabela empresa 'empresa' => array(self::BELONGS_TO, 'Empresa', 'empresa_id'), onde 'empresa' seria a tabela 'Empresa' o Gii identificou que já existe a classe Empresa??? Ou o que seria? 'empresa_id' seria a chave primária, da tabela empresa, que é o relacionamento. Criando todos os arquivos Models com classes models, terei algo similar ao arquivo da Classe Usuarios... indicando o relacionamento com a tabela empresa. Mas irei ter que fazer todos os CRUDs manualmente (controller´s e views) :S POSSIBILIDADE Uma possibilidade é tirar a chave primária de todas as tabelas e gerar o model e CRUD de todas tabelas. Depois de gerado o CRUD (arquivos Controllers e Views) novamente no BD faço o relacionamento. Ai entra a questão: como faço manualmente o relacionamento na classe de cada model? A unica alteração seria acrescentar o código abaixo em cada um dos arquivos, para indicar o relacionamento? public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'empresa' => array(self::BELONGS_TO, 'Empresa', 'empresa_id'), ); } Compartilhar este post Link para o post Compartilhar em outros sites
Williams Duarte 431 Denunciar post Postado Julho 23, 2014 public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'empresa' => array(self::BELONGS_TO, 'Empresa', 'empresa_id'), ); } Sim! Se quiser relacionar com mais, é só declarar em outro array. return array( 'empresa' => array(self::BELONGS_TO, 'Empresa', 'empresa_id', 'alias' => 'apelido'), 'outra' => array(self::BELONGS_TO, 'Empresa', 'outra_id', 'alias' => 'outro_apelido') ); Andei estudando o Yii, antes de optar por outro framework, gerei algumas models, é vi que é difícil manutenção desses códigos gerados. Compartilhar este post Link para o post Compartilhar em outros sites
Giihh 6 Denunciar post Postado Julho 24, 2014 Fiz todo o procedimento criou todos os CRUDs e pastas. Depois inseri manualmente a linha de relacionamento no model de cada um public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'empresa' => array(self::BELONGS_TO, 'Empresa', 'empresa_id'), ); } A paginas carregam normalmente, mas quando vou cadastrar algum usuario por exemplo: aparece a seguinte excessão: CDbException CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`nomedobd`.`usuarios`, CONSTRAINT `fk_usuarios_empresa1` FOREIGN KEY (`empresa_id`) REFERENCES `empresa` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION). The SQL statement executed was: INSERT INTO `usuarios` (`nome`, `login`, `senha`, `nascimento`, `cpf`, `cnpj`, `end`, `tel`, `cel`, `nivel`) VALUES (:yp0, :yp1, :yp2, :yp3, :yp4, :yp5, :yp6, :yp7, :yp8, :yp9) Como faço para corrigir e como fazer para ter um efeito cascata corretamente, quando excluir uma empresa, excluir todas as referencias nas outras tabelas? pra evitar ficar dados "soltos" Compartilhar este post Link para o post Compartilhar em outros sites
Williams Duarte 431 Denunciar post Postado Julho 24, 2014 Este erro é porque você apagou o campo empresa_id da tabela, mas não excluiu a referencia fk_Usuarios_Empresa. E se quiser que funcione corretamente corrija isso aqui, vai ter que incluir empresa_id para inserir o id INSERT INTO `usuarios` (`nome`, `login`, `senha`, `nascimento`, `cpf`, `cnpj`, `end`, `tel`, `cel`, `nivel`) Não posso lhe ajudar mais do que isso, pois só fiz alguns testes com este Framework Yii. ;) Segue um print de como excluir todos os id que pertence ao relacionamento no modo automático, se clicar com botão direito em cima fk_Usuarios_Empresa, vai aparecer a opção de delete da mesma. http://postimg.org/image/u664wwp01/ Compartilhar este post Link para o post Compartilhar em outros sites
Giihh 6 Denunciar post Postado Julho 25, 2014 Williams, estou aprendendo muito aqui nesse tópico, toda ajuda é bem vinda. :D Vou continuar tentando fazer o crud que entendo que o Gii não faz... por exemplo, ele faz o CRUD onde não tem mais de uma chave primaria. No caso da tabela usuarios entendo que é um relacionamento HAS_Many.... conforme o diagrama que passei acima. Então seria uma empresa para muitos usuários certo? Ainda sobre o relacionamento, fiquei com a seguinte duvida: Funcionário pega 1 notebook e 1 celular e devolve depois de um tempo em uma das unidades. ou Notebook ou celular ficam em manutenção. Nesses dois caso, vejo como mudança de status no sistema É necessario criar tabelas para essas "transações de status e localização (tabela unidades)" entre funcionario notebook e celular, como também manutenção? Como guardar essas informações temporárias? Compartilhar este post Link para o post Compartilhar em outros sites
Williams Duarte 431 Denunciar post Postado Julho 25, 2014 No meu caso, não tenho dó de usar tabelas, porem elas são bem relacionadas e simplifico o máximo possível. Veja Você tem duas tabelas praticamente idênticas, notebook e celular, poderia se chamar manutencao e ter um campo do tipo enum() que definiria se é notebook, celular ou outros. tabela usuarios, geralmente usadas para login, definiria nela se é funcionário ou cliente, e criaria outra(s) para endereços, telefones etc. Sobre a tabela empresa cria um empresa_has_usuarios, onde guardaria o id do usuário e empresa. Do mais se necessitar de outra tabela crie. ;) Mas cada um tem um jeito de trabalhar. E vale a máxima do Vale do Silício "Simplify Stupid" PS: Não se preocupe e gerar códigos rapidamente, se fizer um relacionamento bem feito no DB, e seu sistema precisar de implementações futuras, não terá dor de cabeça. Compartilhar este post Link para o post Compartilhar em outros sites
Giihh 6 Denunciar post Postado Julho 25, 2014 Willian realmente foi isso que decidi fazer, deixei o Yii e qualquer framework de lado por enquanto, o Yii por ser de dificil manutenção como você disse, provavelmente por eu saber o básico do básico rsrs, mas tem muitos recursos dele que gostei, como por exemplo getbootstrap já em php adaptado ao Yii e outras extensões (tem um curso em espanhol no youtube que é muito bom, mas as vezes fico com umas duvidas do que ele realmente falou ^_^ ) Acho que aprendi o que você mencionou sobre o relacionamento, e modifiquei tudo. O que entendo é que removendo uma empresa todos os dados relacionados ao ID dela vão ser deletados nas outras tabelas também, já que quando modifiquei o relacionamento em nenhuma das tabelas apareceu a opção CASCADE. Com a nova estrutura se deletar um ID usuario, por exemplo, também será deletada as unidades e notebooks e celulares por ter o relacionamento na tabela com o ID usuario??? Compartilhar este post Link para o post Compartilhar em outros sites
Williams Duarte 431 Denunciar post Postado Julho 26, 2014 Com a nova estrutura se deletar um ID usuario, por exemplo, também será deletada as unidades e notebooks e celulares por ter o relacionamento na tabela com o ID usuario??? Só será deletado na tabela que você configurar o delete em cascata! Analisando melhor sua estrutura, vejo que você precisa de algo bem simples. http://prntscr.com/46drhn Baixe https://www.dropbox.com/s/1graok22j59zgfk/DB.rar Campos de bacos de dados, não pode ter espaços e acentos substitua o espaço por _ Segue um video de como estrutura bem feita. Compartilhar este post Link para o post Compartilhar em outros sites
Giihh 6 Denunciar post Postado Julho 26, 2014 Mas usuarios e funcionarios são pessoas distintas.... funcionarios não acessam o sistema. Usuários (do sistema) funcionarios (da empresa) o sistema deve gerenciar a data que o funcionario pegou e a data que ele devolveu, enquanto os equipamentos estiverem em uso no sistema o status é de "em uso", o funcionario pode pegar os dois equipamentos em uma das unidades e devolver em outra, ou na mesma unidade que pegou. Deve tambem gerenciar os celulares e notebooks que não estao em uso informando em qual unidade esta. Como também gerenciar o status deles caso estiverem em manutenção, informando a data de entrada e saída da manutenção Por isso coloquei a relação e alguns ENUMs na tabela anterior pra modificação de status. As paginas do sistema - Inicio - Configurações - dados e acesso empresa - criar, alterar e excluir usuários - Cadastros - cadastro de funcionarios - cadastro de notebooks - cadastro de celulares - cadastro de unidades (filiais da empresa) - Localização - Localização de notebooks (nas unidades) - Localização de celulares (nas unidades) - Fora das unidades (celular notebook na responsabilidade de algum funcionário) - Manutenção - Celulares em manutenção (data de entrada e de saída) ao sair da manutenção tem que ficar disponivel no sistema - Notebook em manutenção (data de entrada e de saída)ao sair da manutenção tem que ficar disponivel no sistema - Relatórios - Aqui preciso de indicações pra gerar PDF do resultado da pesquisa personalizada, vi um monte de tópico no fórum, mas tenho duvida sobre o que esta depreciado ou nao. Compartilhar este post Link para o post Compartilhar em outros sites
Williams Duarte 431 Denunciar post Postado Julho 26, 2014 #31 :thumbsup: Gerar PDF´s a partir do html MPDF DOMpdF html2pdf este é top! Compartilhar este post Link para o post Compartilhar em outros sites
Giihh 6 Denunciar post Postado Julho 26, 2014 Tank´s Williams Manutenção é do tipo Enum, hoje um notebook pode não estar em manutenção, mas amanhã sim. Necessitando de alteração do status, vou lá e faço um update no select do form e no sistema fica visível um novo status (sim, ou não)... até aqui Ok. Mas e quando for necessário gerar relatórios de datas anteriores? Como faço para ter um controle das alterações? por hora, dia, mês, período, por exemplo. Compartilhar este post Link para o post Compartilhar em outros sites
Williams Duarte 431 Denunciar post Postado Julho 26, 2014 Mas e quando for necessário gerar relatórios de datas anteriores? Como faço para ter um controle das alterações? por hora, dia, mês, período, por exemplo. Você pode trabalhar com Triggers, toda vez que tiver uma ação na tabela, seja UPDATE, INSERT OU DELETE guarda esta informações em outra. Exemplo: /* BEGIN - gatilho_manutencao */ DELIMITER ; DROP TRIGGER IF EXISTS `gatilho_manutencao`; DELIMITER ;; CREATE TRIGGER `gatilho_manutencao` AFTER UPDATE ON `manutencao` FOR EACH ROW BEGIN INSERT INTO `manutencao_historico` SET `id` = NEW.`campo_tabela_manuntencao_id`, `campo1` = NEW.`campo_tabela_manuntencao_campo1`, `campo2` = NEW.`campo_tabela_manuntencao_campo2`, `created` = NOW(); /* campo do tipo datetime */ END ;; DELIMITER ; /* END - gatilho_manutencao */ Existem duas formas de pegar as informações OLD e NEW No caso ai é new = novo Compartilhar este post Link para o post Compartilhar em outros sites