Yii Framework Forum: Duda Consulta Sql Relacional (Criteria) Con Varias Tablas (Más De Dos) - Yii Framework - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Duda Consulta Sql Relacional (Criteria) Con Varias Tablas (Más De Dos) - Yii Framework Duda consulta SQL relacional (Criteria) con varias tablas (más de dos) Rate Topic: -----

#1 User is offline   Luis Guillermo Trejo 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 31
  • Joined: 29-March 12

Posted 11 November 2012 - 01:44 AM

Buena noche, he trabajado un poco con consultas relacionales entre dos tablas, por medio de las relaciones creadas en el modelo que Yii genera automáticamente, más en este momento requiero hacerlo con tres tablas las cuales son las siguientes (los puntos suspensivos representan campos de poca relevancia):

-Lineas (id_linea, linea...........)

-Referencias (id_referencia, id_linea, referencia, imagen (este campo guarda una ruta de la imagen).............)

-Inventario (id_inventario, id_referencia, cantidad, fecha, estado, ..............)

Los siguientes son los métodos "relations" y "search" del modelo de la tabla "inventario" (cuyo CRUD completo lo genere por gii)
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(
                        'creadoPor' => array(self::BELONGS_TO, 'Usuarios', 'creado_por'),
			'editadoPor' => array(self::BELONGS_TO, 'Usuarios', 'editado_por'),
			'idReferencia' => array(self::BELONGS_TO, 'Referencias', 'id_referencia'),
                        'referencia' =>array(self::BELONGS_TO, 'Referencia', 'id_referencia'),
                        'creador' =>array(self::BELONGS_TO, 'Usuario', 'creado_por'),
                        'editor' =>array(self::BELONGS_TO, 'Usuario', 'editado_por'),
		);
	}

public function search()
	{
		// Warning: Please modify the following code to remove attributes that
		// should not be searched.

		$criteria=new CDbCriteria;
                
                $criteria->condition = "estado='Ingreso'";
		$criteria->compare('id_inventario',$this->id_inventario);
		$criteria->compare('id_referencia',$this->id_referencia);
		$criteria->compare('cantidad',$this->cantidad);
		$criteria->compare('estado',$this->estado,true);
		$criteria->compare('fecha',$this->fecha,true);
                $criteria->compare('creado_por',$this->creado_por);
		$criteria->compare('creado_en',$this->creado_en,true);
		$criteria->compare('editado_por',$this->editado_por);
		$criteria->compare('editado_en',$this->editado_en,true);

		return new CActiveDataProvider($this, array(
			'criteria'=>$criteria,
		));
	}


La acción "admin" (del controlador) para usar el buscador, está intacta (no lo he modificado).
public function actionAdmin()
	{
		$model=new Inventario('search');
		$model->unsetAttributes();  // clear any default values
                
                $lineas = $model->obtenerLineas();
                $usuarios = $model->obtenerUsuarios();
                
		if(isset($_GET['Inventario']))
			$model->attributes=$_GET['Inventario'];

		$this->render('admin',array(
			'model'=>$model, 'lineas'=>$lineas, 'referencias'=>$referencias, 'usuarios'=>$usuarios,
		));
	}


Y la vista contiene dos listas desplegables la segunda es dependiente de la primera y funciona sin problema, la hice basado en este link http://www.yiiframew...ndent-dropdown/,

<div class="wide form">

<?php $form=$this->beginWidget('CActiveForm', array(
	'action'=>Yii::app()->createUrl($this->route),
	'method'=>'get',
)); ?>
        
        <div class="row">
                <?php echo CHtml::label('Línea', 'Línea'); ?>
                <?php
                echo CHtml::dropDownList('id_linea','', CHtml::listData(Linea::model()->findAll($lineas),'id_linea','linea'),
                    array('empty'=>'',
                        'ajax' => array(
                            'type'=>'POST', //request type
                            'url'=>CController::createUrl('inventario/obtenerReferencias'),
                            'update'=>'#Inventario_id_referencia', //selector to update
                        )
                    )
                );
                ?>
        </div>
        
        <div class="row">
                <?php echo $form->label($model,'id_referencia'); ?>
                <?php echo CHtml::dropDownList('Inventario[id_referencia]','Inventario_id_referencia', array(),array('empty'=>'')); ?>
        </div>

	<div class="row buttons">
		<?php echo CHtml::submitButton('Buscar'); ?>
	</div>

<?php $this->endWidget(); ?>

</div><!-- search-form -->


solo deje dos campos en el formulario de búsqueda, pues solo me pidieron esos, una vez seleccionó una línea, me despliega (por medio de la petición ajax) en la otra lista las referencias relacionadas a esta línea y una vez seleccionó una referencia, puedo consultar sin problema los inventarios relacionados a la referencia, más requiero que si se selecciona una línea, pero no una referencia, se puedan consultar todos los inventarios relacionados a las líneas "hijas" de la referencia.

Por si les es útil (más no creo que necesario mencionar en relación a mi necesidad) les dejo la acción que se ejecuta en la petición ajax:
public function actionObtenerReferencias()
        {
            $data=Referencia::model()->findAll('id_linea=:id_linea', 
                          array(':id_linea'=>(int) $_POST['id_linea']));

            $data=CHtml::listData($data,'id_referencia','referencia');
            echo CHtml::tag('option',
                           array('value'=>''),CHtml::encode(''),true);
            foreach($data as $value=>$name)
            {
                echo CHtml::tag('option',
                           array('value'=>$value),CHtml::encode($name),true);
            }
        }


y la función del modelo con la cual consultó las referencias que se cargan por defecto en la primera lista desplegable:
public function obtenerReferencias()
        {
                $criteria = new CDbCriteria;
                $criteria->select = "id_referencia, referencia";
                $criteria->order = "referencia";
                return $criteria;
        }


Supongo que hay que modificar el método "search" del modelo, más no tengo claro cómo hacerlo, he leído sobre las consultas con criteria, más aún me parecen un poco confusas, si alguno de uds me puede ayudar con esto, se lo agradecería mucho.

De igual manera les agradecería si alguno sabe cómo imprimir de forma "100% limpia" (no un array, ni nada ambiguo, como ya he visto en muchos posts) las consultas ejecutadas, por criteria.

Gracias por su atención.
0

#2 User is offline   robregonm 

  • Expert Yii Developer
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 595
  • Joined: 30-July 09
  • Location:Colombia

Posted 11 November 2012 - 12:05 PM

Antes que nada le recomiendo usar un generador un poco más potente que el generador por defecto (personalmete uso AweCrud) que ayudan a generar un código más "completo" en el sentido de consultas campos relacionales.
En cuanto a la función search() y los criteria, son un concepto bastante usado (no solamente en Yii sino en buena cantidad de frameworks), así que conviene conocerlo.
Un criteria es una especie de "extensión" o "restricción" a una consulta "SELECT * FROM MiTabla", que es bastante genérica.
Los criteria permiten agregarle a esa consulta las relaciones (sentencia join o with), condiciones del where (sentencia condition), agrupamiento (group), parámetros (param), entre muchos otros....
OJO: esas condiciones por sí solas no hacen nada, se tienen que aplicar a un modelo o a una búsqueda o a un provider... Entonces podríamos llegar a hacer algo como:
$this->getDbCriteria()->mergeWith($criteria);
return $this;


En el caso de las condiciones tipo LIKE, se hacen con la sentencia "compare" del criteria, por eso que en el search encuentra varias de estas sentencias.
Entonces, lo que sucede es que el CGridView utiliza la función "search()" para hacer sus búsquedas, de ahí la importancia de esta función. Pero no hay mayor ciencia detrás de esa función.
Espero haber resuelto algunas dudas.
Ricardo Obregón

YiiFramework en Español - http://yiiframework.co/ - http://yiiframeworkenespanol.org/ - Yii Code Generator for Bootstrap
http://obregon.co/ - https://1server.co/
PHP 5.5+, nginx 1.7, MySQL(MariaDB & PerconaDB), PostgreSQL 9, Yii 2, CanJS
Follow me: @robregonm & @obregonco & @1ServerCo.
0

#3 User is offline   robregonm 

  • Expert Yii Developer
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 595
  • Joined: 30-July 09
  • Location:Colombia

Posted 11 November 2012 - 12:06 PM

Por cierto, qué quiere decir o a qué se refiere cuando dice:

Quote

De igual manera les agradecería si alguno sabe cómo imprimir de forma "100% limpia" (no un array, ni nada ambiguo, como ya he visto en muchos posts) las consultas ejecutadas, por criteria.

Saludos.
Ricardo Obregón

YiiFramework en Español - http://yiiframework.co/ - http://yiiframeworkenespanol.org/ - Yii Code Generator for Bootstrap
http://obregon.co/ - https://1server.co/
PHP 5.5+, nginx 1.7, MySQL(MariaDB & PerconaDB), PostgreSQL 9, Yii 2, CanJS
Follow me: @robregonm & @obregonco & @1ServerCo.
0

#4 User is offline   Luis Guillermo Trejo 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 31
  • Joined: 29-March 12

Posted 11 November 2012 - 12:30 PM

Hola robregonm, gracias por la respuesta, más debo admitir que me siento "en las mismas" pues más que teoría, buscaba es código, sé que la teoría es importante, más también el código respecto a la consulta que necesito realizar, de igual aclaro, con lo de imprimir SQL, me refiero es a qué de forma "típica" se puede crear consultas SQL a manera de cadena y "meter" esa cadena en una variable, para luego llamar un método de tipo "execute" al cual se le pasa como parámetro la variable (que contiene la cadena) para ejecutar el query en la BD, más esa variable es posible imprimirla retornando la cadena SQL por completo, para depurar la consulta que finalmente se ejecuta. soy claro?
0

#5 User is offline   robregonm 

  • Expert Yii Developer
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 595
  • Joined: 30-July 09
  • Location:Colombia

Posted 13 November 2012 - 11:40 AM

Ah ya entendí... jajaja... no había comprendido, pero bueno... a lo q se refiere es pasar por alto el ActiveRecord y utilizar el DAO... mejor dicho, hacer una consulta de forma directa a la BD en lenguaje SQL, correcto?
Bueno, se hace algo así:
Yii::app()->db->createCommand()->select('campos')->from('tablas')->where('condiciones')->group(...)->execute()

Lo anterior equivale al típico SELECT campos FROM tablas WHERE condiciones GROUP... (Era de esperarse, no?)
Un poco extenso pero espero haber cubierto una buena cantidad de opciones... porque igual se pueden hacer consultas sencillas del tipo:
Yii::app()->createCommand('SELECT * FROM tablas')


Pero por escalabilidad se prefiere la primera opción.
Ricardo Obregón

YiiFramework en Español - http://yiiframework.co/ - http://yiiframeworkenespanol.org/ - Yii Code Generator for Bootstrap
http://obregon.co/ - https://1server.co/
PHP 5.5+, nginx 1.7, MySQL(MariaDB & PerconaDB), PostgreSQL 9, Yii 2, CanJS
Follow me: @robregonm & @obregonco & @1ServerCo.
0

#6 User is offline   robregonm 

  • Expert Yii Developer
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 595
  • Joined: 30-July 09
  • Location:Colombia

Posted 13 November 2012 - 11:49 AM

Depronto cabría agregar que el DAO (o consultas por SQL directo) no deberían hacerse a menos que haya una buena razón para eso, pues estoy seguro que despues de que haya aprenddo a usar ActiveRecord no querrá usar DAO a menos que exista esa buena razón, jajaja
Con ActiveRecord es mucho más sencillo hacer varias cosas.
Por ejemplo una consulta relacional podría ser algo como:
$CreadorInventario = Inventario::model()->findByPk(1)->creadoPor->nombre;


Lo anterior se traduciría como dos consultas (también se le puede indicar que lo haga en una sola consulta y no dos separadas):
Una sería: SELECT * FROM Inventario WHERE id = 1;
y la otra: SELECT * FROM Usuarios WHERE id = {Valor devuelto en la consulta anterior}
Y la versión para una sola consulta, es decir, que genere algo como: "SELECT * FROM Inventario i LEFT JOIN Usuarios u ON (i.creado_por = u.id) WHERE i.id=1" sería entonces algo como:
$creadorInventario = Inventario::model()->with('creadoPor')->together()->findByPk(1)->nombre;



Saludos
Ricardo Obregón

YiiFramework en Español - http://yiiframework.co/ - http://yiiframeworkenespanol.org/ - Yii Code Generator for Bootstrap
http://obregon.co/ - https://1server.co/
PHP 5.5+, nginx 1.7, MySQL(MariaDB & PerconaDB), PostgreSQL 9, Yii 2, CanJS
Follow me: @robregonm & @obregonco & @1ServerCo.
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users