php - transaction - Laravel 4 Eloquent devuelve una identificación incorrecta
where and laravel (3)
Si ejecuta esta consulta en phpMyAdmin, probablemente debería poder ver que el resultado contiene varias columnas con el nombre " id ". Cuando PHP analiza el resultado de la consulta a una matriz u objeto asociativo, ¡las claves deben ser únicas! Si las llaves están colisionando, ¡se usará la última columna!
Ejemplo:
Resultado de SQL:
id user_id name id name company_id
1 2 Camp1 2 Pelle 1
Resultado de PHP:
array (size=1)
0 =>
object(stdClass)[131]
public ''id'' => string ''2'' (length=1)
public ''user_id'' => string ''2'' (length=1)
public ''name'' => string ''Pelle'' (length=5)
public ''company_id'' => string ''1'' (length=1)
Para resolver esto, puede agregar una cláusula de selección para seleccionar solo las columnas de la campaña:
Campaign::select(''campaigns.*'')
->join(''users'', ''users.id'', ''='', ''campaigns.user_id'')
->where(''users.company_id'', ''='', Auth::user()->company->id)
->where(''campaigns.id'', ''='', Input::get(''id''))
->first();
Tengo 3 tablas en mi base de datos:
- Campañas
- Usuarios
- Compañías
Una empresa puede tener algunos usuarios. Un usuario puede tener algunas campañas. Un usuario (con derechos de administrador) puede realizar algunas acciones con cualquier campaña que pertenezca a su empresa. Entonces, quiero verificar si está haciendo estas acciones con su campaña o no (en el último caso, devuelvo algo como "acceso denegado").
Mi Condición
Campaign::join(''users'', ''users.id'', ''='', ''campaigns.user_id'')
->where(''users.company_id'', ''='', Auth::user()->company->id)
->where(''campaigns.id'', ''='', Input::get(''id''))
->first();
Entonces, si obtuve una campaña única, está bien, si obtuve una nulidad, algo está mal y le envío "acceso denegado" al usuario, ya que está tratando con otra campaña de la compañía.
Este código produce la próxima consulta:
array(3) {
["query"]=>
string(148) "select * from `campaigns` inner join `users` on `users`.`id` = `campaigns`.`user_id` where `users`.`company_id` = ? and `campaigns`.`id` = ? limit 1"
["bindings"]=>
array(2) {
[0]=>
string(1) "2"
[1]=>
string(2) "13"
}
["time"]=>
float(0.42)
}
Usando phpmyadmin Probé la misma consulta y obtuve una campaña con ID = 13. Pero cuando estaba depurando mi aplicación descubrí que
dd($campaign->id);
devuelve 8 en su lugar. 8 también es igual a campaigns.user_id
(El registro tiene tanto campaigns.id
como campaigns.user_id = 8
).
No puedo entender por qué está sucediendo. Incluso si algo está mal con mi consulta SQL (lo que dudo que phpmyadmin haya arrojado los resultados correctos), obtuve la condición campaigns.id = Input::get(''id'')
, donde Input::get(''id'') = 13
. ¿Por qué se está cambiando el ID?
Por supuesto que puedo hacer este chequeo de seguridad en dos pasos, como primero obtener la campaña, luego verificar $campaign->user->company->id = Auth::user()->company->id
pero me pregunto ...
Esto parece una limitación en la biblioteca Eloquent. En lugar de usar la última "identificación", debería ser más proactivo al buscar el "id" de la tabla principal de la consulta. O use "como" en las declaraciones de SQL.
Otra solución sería nombrar el campo de identificación de cada tabla de manera única. por ejemplo, user.user_id, campaigns.campaign_id. Esto colisionaría con las claves externas en otras tablas, así que nombre las claves externas como campaigns.user_fid.
Si desea controlar la tabla real de quién es la columna duplicada, simplemente agregue las tablas a una matriz en un orden específico. El último será la propiedad ->id
.
->select([
''table1.*'',
''table2.*'',
''tags.*'',
''companies.*'',
''users.*'',
])
De esta manera, conservas todas las columnas exclusivas que se seleccionan con la join
. Si desea columnas específicas de una tabla específica, puede usar un AS
:
->select([
''table1.*'',
''table2.*'',
''tags.*'',
''companies.*'',
''users.*'',
''table1.id AS table1_id'',
''tags.name AS tag_name'',
''companies.name AS company_name'',
])