我的一个项目遇到了一些障碍。像很多书呆子一样,我决定创建自己的视频游戏评论网站。评论存储在数据库中,可以使用以下网址通过游戏标题检索:
http://www.example.com/reviews/ {gameName} / {可选 pageOfReview}
不幸的是,在测试边缘情况时,我遇到了一个奇怪的错误 - 如果游戏标题中有一段时间我无法检索它。如果句点是标题的主要字符(如.hack),我会得到一个Kohana堆栈跟踪错误屏幕,告诉我评论(或者更确切地说,游戏)不存在。如果它位于标题的中间或末尾,我会收到一条我自己的错误消息,指出无法检索到评论(游戏)。有没有办法解决?这是一个关于MySQL如何解析一段时间或其他问题的问题吗?
编辑:使用MySQLi驱动程序通过Kohana 2的ORM功能处理所有查询。保存评论(管理员控制器):
public function saveReview()
{
$this->checkAdmin();
if (isset($_POST['submit'])) { $this->storeReview(); }
else { header('Location: /admin'); }
}
private function storeReview($id = null)
{
if (!preg_match("/^[a-zA-Z0-9\-_:!'. ]*$/", $_POST['gameTitle']) || empty($_POST['gameTitle'])) { $gameTitle = false; }
else { $gameTitle = ucwords($this->clean($_POST['gameTitle'])); }
if (!is_numeric($_POST['genre'])) { $genre = false; }
else { $genre = $_POST['genre']; }
$platformCheckArray = array_map('is_numeric', $_POST['platforms']);
$platformCheck = true;
foreach ($platformCheckArray as $pca)
{
if (!$pca)
{
$platformCheck = false;
break;
}
}
$proCheck = true;
$cleanedPros = array();
foreach ($_POST['pros'] as $pro)
{
if (!preg_match("/^[a-zA-Z0-9\-_:!' ]*$/", $pro))
{
$proCheck = false;
break;
}
if (!empty($pro)) { $cleanedPros[] = $this->clean($pro); }
}
$conCheck = true;
$cleanedCons = array();
foreach ($_POST['cons'] as $con)
{
if (!preg_match("/^[a-zA-Z0-9\-_:!' ]*$/", $con))
{
$conCheck = false;
break;
}
if (!empty($con)) { $cleanedCons[] = $this->clean($con); }
}
if (!is_numeric($_POST['score'])) { $score = false; }
else { $score = $_POST['score']; }
if (empty($_POST['content'])) { $content = false; }
else { $content = true; }
// save review if all tests pass, display error otherwise
if ($gameTitle && $genre && $platformCheck && $proCheck && $conCheck && $score && $content)
{
$gameTitle = $gameTitle;
$platforms = $_POST['platforms'];
$reviewContent = $_POST['content'];
$prosText = implode(', ', $cleanedPros);
$consText = implode(', ', $cleanedCons);
$game = ORM::factory('game');
$game->title = $gameTitle;
$game->genre_id = $genre;
$game->platforms = $platforms;
$game->save();
$storedGenre = ORM::factory('genre')->where('id', $genre)->find();
$storedGenre->platforms = $platforms;
$storedGenre->save();
$review = ORM::factory('review', $id);
$review->content = $reviewContent;
$review->score = $score;
$review->game_id = $game->id;
$review->date_added = date('Y-m-d H:i:s');
$review->platforms = $platforms;
$review->save();
$pros = ORM::factory('pro');
$pros->review_id = $review->id;
$pros->text = $prosText;
$pros->save();
$cons = ORM::factory('con');
$cons->review_id = $review->id;
$cons->text = $consText;
$cons->save();
if ($game->saved && $storedGenre->saved && $review->saved && $pros->saved && $cons->saved) { $this->success('review'); }
else { $this->showError("Something went wrong with saving the review. Please try again."); }
}
else { $this->showError("All fields must contain values. Please try again."); }
}
检索评论(来自评论控制者):
public function show($id, $page = 1)
{
if (is_numeric($id)) { $game = ORM::factory('game', $id); }
else
{
$id = ucwords(stripslashes($id));
$game = ORM::factory('game')->where('title', $id)->find();
}
if ($game->loaded) { $this->showReview($game->id, $page); }
else { HandiError::factory('Could not retrieve the specified review. Please check that you entered the correct value.'); }
}
private function showReview($id, $page = 1)
{
$page = (int)$page;
if ($page < 1) { $page = 1; }
if ($id)
{
$game = ORM::factory('game', $id);
$review = ORM::factory('review')->where('game_id', $game->id)->find();
$genre = ORM::factory('genre')->where('id', $game->genre_id)->find();
$revPlatforms = $this->db->query("SELECT * FROM platforms
INNER JOIN platforms_reviews AS pr ON platforms.id = pr.platform_id
INNER JOIN reviews ON pr.review_id = reviews.id
WHERE reviews.id = ?", $review->id);
$revPros = ORM::factory('pro')->where('review_id', $review->id)->find();
$revCons = ORM::factory('con')->where('review_id', $review->id)->find();
$platforms = array();
foreach($revPlatforms as $rp) { $platforms[] = $rp->name; }
$pros = explode(', ', $revPros->text);
$cons = explode(', ', $revCons->text);
$pages = explode('<split />', $review->content);
$count = count($pages);
if ($page > ($count)) { $content = $pages[0]; }
else { $content = $pages[$page - 1]; }
$view = new View('reviews/show_review');
$view->content = $content;
$view->gameTitle = $game->title;
$view->genre = $genre->name;
$view->platforms = implode(', ', $platforms);
$view->pros = $pros;
$view->cons = $cons;
$view->score = $review->score;
$view->pages = $pages;
$view->render(true);
}
else { HandiError::factory('Could not retrieve the specified review. Please check that you entered the correct value.'); }
}
编辑2:嗯,我发现了一些关于领先时期的案例:
在我的控制器索引中,我有一些查询用于按游戏标题,平台,流派等列出评论。这基本上是一个穷人的维基。参见:
public function index()
{
/* show a wiki-like page with reviews listed by title,
* game title, genre, and platform
*/
$numGenres = $this->db->query("SELECT COUNT(id) AS num FROM genres");
$numPlatforms = $this->db->query("SELECT COUNT(id) AS num FROM platforms");
$genreCount = $numGenres[0]->num;
$platformCount = $numPlatforms[0]->num;
$scoreCount = 5;
$genreResults = array();
$platformResults = array();
$scoreResults = array();
$gameResults = $this->db->query("SELECT LEFT(title, 1) AS letter, COUNT(id) AS count FROM games GROUP BY letter ORDER BY letter ASC");
for($i = 1; $i < ($genreCount + 1); ++$i)
{
$genreResults[] = $this->db->query("SELECT genres.id AS id, genres.name AS name, COUNT(reviews.id) AS num FROM reviews
INNER JOIN games ON reviews.game_id = games.id
INNER JOIN genres ON games.genre_id = genres.id
WHERE genres.id = ?", $i);
}
for($j = 1; $j < ($platformCount + 1); ++$j)
{
$platformResults[] = $this->db->query("SELECT platforms.id AS id, platforms.name AS name, COUNT(reviews.id) AS num FROM reviews
INNER JOIN platforms_reviews AS pr ON reviews.id = pr.review_id
INNER JOIN platforms ON pr.platform_id = platforms.id
WHERE platforms.id = ?", $j);
}
for($k = 1; $k < ($scoreCount + 1); ++$k)
{
$scoreResults[] = $this->db->query("SELECT score, COUNT(id) AS num FROM reviews WHERE score = ?", $k);
}
$view = new View('reviews/index');
$view->gamesByLetter = $gameResults;
$view->genres = $genreResults;
$view->platforms = $platformResults;
$view->scores = $scoreResults;
$view->render(true);
}
当我将这些查询的结果传递给视图时,我遍历它们并根据元类别创建链接。因此,它显示有多少游戏以字母A,B等开头,点击其中一个链接会将用户带到一个链接列表,每个链接都有一个评论(所以,A-&gt; Afterburner(等等) ) - &gt;审查Afterburner)。
当我将鼠标悬停在具有前导期间的组上时,我的状态栏会显示链接中缺少的时间段,即使它显示在源中。因此,即使源显示链接为site.com/reviews/game/。浏览器将其显示为site.com/reviews/game/这让我相信这段时间甚至没有被传递到方法中,并且堆栈跟踪似乎已经确认(它声称有一个缺失的参数,这将是期间)。
编辑3:好的,我看了一下我的路线,在那里找不到任何东西。也就是说,我确实有一个.htaccess文件,mod_rewrites路由看起来很漂亮的SEO,所以我想知道这可能是问题。我自己从未写过一个mod_rewrite文件 - Kohana论坛上的人给了我这个,并且它有效,所以我去了。我可以理解一些涉及的regEx,但我的regEx Fu很弱。我相信最后一行是'魔术'。# Turn on URL rewriting
Options +FollowSymlinks
RewriteEngine On
# Put your installation directory here:
# If your URL is www.example.com/, use /
# If your URL is www.example.com/kohana/, use /kohana/
RewriteBase /
# Do not enable rewriting for files or directories that exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# For reuests that are not actual files or directories,
# Rewrite to index.php/URL
# Original rule:
# RewriteRule ^(.*)$ index.php/$1 [PT,L]
# Alternative rule:
# RewriteRule .* index.php/$0 [PT,L]
# 2nd alternative rule that works on ICDSoft:
RewriteRule .* index.php?kohana_uri=$0 [PT,QSA,L]
如果我正在读这个,'。'只是意味着任何一个角色。
可以'。'除了表示文件扩展名或Web后缀(.com,.org等)之外,还可以在格式良好的URL中使用?我的意思是,当我将鼠标悬停在与他们的链接上时,他们没有出现在Firefox的状态栏中,这让我相信这是一个浏览器/格式良好的问题,而不是编码问题。
答案 0 :(得分:0)
MySQL列数据中的句点没有问题。但是,句点用于将表名与列名分开:table.column
。如果您的查询未正确转义和引用,则该句点可能会被错误地解释为表/列分隔符。
您是如何准备查询的?
答案 1 :(得分:0)
我认为,这个问题出现在Kohana框架中,而不是SQL中。从网址过滤参数。尝试打印您的查询,看看它在执行时的确切内容,并观察您的期间发生的事情。
答案 2 :(得分:0)
只是一个猜测,但你设置了你的路线以允许在网址中的句号?默认情况下,Kohana不允许使用句点。您需要将Route :: set()的第三个参数设置为以下内容:
Route::set('reviews', 'reviews/<name>(/<page>)', array('name' => '[^/,;?]++', 'page' => '\d+')
->defaults(array(
'controller' => 'reviews',
'action' => 'load',
'name' => NULL,
'page' => 1,
));
参见论坛帖子http://forum.kohanaframework.org/comments.php?DiscussionID=4320
答案 3 :(得分:0)
使用Profiler检查所有生成的查询的时间。
在Controller :: __ construct()中放入
新的Profiler;
并找到可能已损坏的查询。
其他可能的解决方案: 通过您的代码,有时未关闭/未终止的数据库查询实例可能会破坏(或合并)其他查询......