我正在创建一个管理系统,教师可以管理学生的最终项目,而前者可以看到其他学生创建的内容
我是一个laravel新手,我有优化查询和验证网址的问题
这是我的表模式:
Cursos
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| curso | varchar(255) | NO | | NULL | |
+-------+------------------+------+-----+---------+----------------+
Trienios
+--------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| data_trienio | varchar(255) | NO | | NULL | |
| curso_id | int(11) | NO | | NULL | |
| oe_id | int(11) | NO | | NULL | |
+--------------+------------------+------+-----+---------+----------------+
Alunos
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| id_cartao | int(10) unsigned | NO | UNI | NULL | |
| nome | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | UNI | NULL | |
| trienio_id | int(11) | NO | | NULL | |
+------------+------------------+------+-----+---------+----------------+
PAP
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| nome | varchar(255) | NO | | NULL | |
| descricao | text | NO | | NULL | |
| nota | int(11) | NO | | NULL | |
| aluno_id | int(11) | NO | | NULL | |
+-----------+------------------+------+-----+---------+----------------+
到目前为止,我已经设法根据cursos和trienios表中定义的记录设置动态URL,如下所示:http:// localhost:8000 / TGEI / 2014-2017
(TGEI是cursos表中的记录,用于获取相关的trienio记录,而2014-2017是trienios表中的记录,该记录与1对多关系中的curso记录相关并获取相关的pap记录)
这一切都很顺利,但我在优化效率极低的查询方面遇到了麻烦,这些查询在数据库增长时将成为一个非常重要的问题
这是我的关系:
Curso.php
public function trienio()
{
return $this->hasMany('App\Trienio');
}
Trienio.php
public function curso()
{
return $this->belongsTo('App\Curso');
}
public function oe()
{
return $this->belongsTo('App\OE');
}
public function aluno()
{
return $this->hasMany('App\Aluno');
}
Aluno.php
public function trienio()
{
return $this->belongsTo('App\Trienio');
}
public function pap()
{
return $this->hasOne('App\PAP');
}
PAP.php
protected $table = 'pap';
public function aluno()
{
return $this->belongsTo('App\Aluno');
}
这些是负责为用户可访问页面提供服务的控制器:
CursoController.php
public function index(Curso $curso)
{
$cursos = $curso->all();
return view('curso')->withCursos($cursos);
}
TrienioController.php
public function index(Trienio $trienio, $curso)
{
$trienios = $trienio->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
})->get();
return view('trienio')->withTrienios($trienios);
}
PapController.php
public function index(Pap $pap, $curso, $trienio)
{
$pap = $pap->whereHas('aluno.trienio', function ($query) use ($curso, $trienio) {
$query->where('data_trienio', '=', $trienio)->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
});
})->toSql();
dd($pap);
return view('pap')->withPap($pap);
}
public function show(Pap $pap, $curso, $trienio, $id)
{
$pap = $pap->find($id);
dd($pap);
return view('show')->withPap($pap);
}
正如您所看到的,在PAP控制器的索引方法的情况下,请求数据的查询是一个巨大的混乱,这是n 1问题的缩影:
"select * from `pap` where exists (select * from `alunos` where `pap`.`aluno_id` = `alunos`.`id` and exists (select * from `trienios` where `alunos`.`trienio_id` = `trienios`.`id` and `data_trienio` = ? and exists (select * from `cursos` where `trienios`.`curso_id` = `cursos`.`id` and `curso` = ?)))"
我打算用这个查询来获取与trienio记录相关的PAP记录,而这些记录又与一个curso记录相关,基于用户在url中输入的输入(我已经在上面显示了一个例子),问题是,因为我一般是这个东西的新手,我无法将急切的加载概念应用于我想要运行的查询
我也在验证用户可以输入以下内容的网址时遇到问题:
http://localhost:8000/qwfkjnfwq/qjqtikjn/1
并且控制器方法show将获取pap记录,而不考虑用户输入2级以上的参数,这显然会造成“安全”问题
我想做的是:
http://localhost:8000/TGEI/2014-2017/1
控制器方法show将加载aluno.trienio嵌套关系,然后根据2014-2017参数获取与aluno模型相关的trienio id,然后根据TGEI参数获取与trienio模型相关的curso id
所以,像这样的东西
http://localhost:8000/qwfkjnfwq/qjqtikjn/1
将失效而不是通过.
这可能是一个棘手的问题,但无论谁能帮助我,我都会感谢.我知道我的问题的某些部分可能不清楚(更重要的是因为英语不是我的第一语言),在这种情况下,我可以根据你的需要澄清它们.
为了更好的信息,这是我的web.php文件
Route::get('/', 'CursoController@index');
Route::get('/{curso}', 'TrienioController@index');
Route::get('/{curso}/{trienio}', 'PapController@index');
Route::get('/{curso}/{trienio}/{id}', 'PapController@show');
最佳答案 好的,请扩展我的评论.
Laravel 5.2来了route model binding,它可以让你在控制器方法中注入模型(像这样:公共函数显示(Pap $pap)),Laravel将自动获取带有url的pap模型(基本上做Pap :: find ($id)并将返回值保存到$pap变量中.这并不总是您想要的,因为您经常要执行更复杂的查询.
我建议你不要在你的情况下使用路由模型绑定,只需自己进行查询.这样的事情(看看我是如何从控制器功能中删除模型的)
// CursoController.php
public function index()
{
$cursos = Curso::all();
return view('curso')->withCursos($cursos);
}
// TrienioController.php
public function index($curso)
{
$trienios = Trienio::whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
})->get();
return view('trienio')->withTrienios($trienios);
}
// Pap controller
public function index($curso, $trienio)
{
$pap = Pap::whereHas('aluno.trienio', function ($query) use ($curso, $trienio) {
$query->where('data_trienio', '=', $trienio)->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
});
})->get();
return view('pap')->withPap($pap);
}
public function show($curso, $trienio, $id)
{
$pap = Pap::whereHas('aluno.trienio', function ($query) use ($curso, $trienio) {
$query->where('data_trienio', '=', $trienio)->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
});
})->findOrFail($id);
return view('show')->withPap($pap);
}
另请注意,在show()方法中,我几乎复制了index()查询,这是验证.
关于查询的优化 – 你拥有它们的查询绝对没问题.没有n 1问题.
如果您要在其中一个索引结果上执行foreach并调用子属性,那么您将遇到n 1问题.例如,如果你在pap视图中做这样的事情:
@foreach($pap as $p)
<div>{{ $p->aluno->id }}</div>
@endforeach
这将为$pap中的每个$p创建一个新查询以获取相关的aluno结果.
要避免这个n 1问题,您必须在循环中使用之前加载数据.您会急切地使用 – > with(relationship)方法加载数据.像这样的东西:
// Pap controller
public function index($curso, $trienio)
{
$pap = Pap::whereHas('aluno.trienio', function ($query) use ($curso, $trienio) {
$query->where('data_trienio', '=', $trienio)->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
});
})
->with('aluno.trienio') // You might need some additional checks here, depending on you needs
->get();
return view('pap')->withPap($pap);
}
它并不完全直观,但是 – > whereHas(关系)不会急于加载关系.所以你经常会发现自己写这样的声明:
// Pap controller
public function index($curso, $trienio)
{
$pap = Pap::whereHas('aluno.trienio', function ($query) use ($curso, $trienio) {
$query->where('data_trienio', '=', $trienio)->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
});
})
->with(['aluno.trienio' => function ($q) use ($curso, $trienio) {
$query->where('data_trienio', '=', $trienio)->whereHas('curso', function ($query) use ($curso) {
$query->where('curso', '=', $curso);
}]); // These are the additional checks
->get();
return view('pap')->withPap($pap);
}