\$\begingroup\$
\$\endgroup\$
12
Could someone please check my query and the table structures and let me know if there is any way to improve it.
I would like some tips for my own personal development
Query in PHP:
// Load all records so can be displayed in list
function load_all($page = NULL, $chat_type = NULL){
if(!$page)$page = 1;
$start = ($page - 1) * ITEMS_PER_PAGE_ADMIN;
$query = "";
$query .= "SELECT direct_message.*, IFNULL(direct_message_group.staff_id, 0) as staff_id FROM `direct_message`
LEFT JOIN direct_message_group ON direct_message_group.chat_id = direct_message.id
INNER JOIN direct_message_thread ON direct_message_thread.chat_id = direct_message.id
WHERE";
$query .= " (
(
direct_message.recipient_id = '".addslashes(session::getAdministratorStaff()->id)."'
OR direct_message.creator_id = '".addslashes(session::getAdministratorStaff()->id)."'
OR (
direct_message_group.staff_id = '".addslashes(session::getAdministratorStaff()->id)."'
AND direct_message_group.active = '0'
)
)
)";
$query .= " AND direct_message.school_id = '".addslashes(session::getAdministratorStaffSchoolID())."'
AND direct_message_thread.school_id = direct_message.school_id
GROUP BY direct_message.id
ORDER BY direct_message_thread.inserted DESC LIMIT $start, ".ITEMS_PER_PAGE_ADMIN;
$rows = database::select($query);
foreach($rows as $row){
$holding = new direct_message();
$holding->_populate($row);
$this->direct_messages[] = $holding;
}
}
Table structures:
---------------------------------------------------
---------------------------------------------------
200_success
145k22 gold badges190 silver badges478 bronze badges
1 Answer 1
\$\begingroup\$
\$\endgroup\$
3
Some thoughts:
- You should consider using prepared statements.
- Remove
$chat_type
as parameter, as it is not used - Consider stronger validation on
$page_type()
parameters(like that is it null or integer). Right now you can easily break the function - Don't use
*
selects. They don not help the code reader understand what sort of data is being returned without having to look a database. They also tend to cause more bandwidth consumption than is necessary and make your queries fragile. What if you add a new audit field to the database table? That could then break your object instantiation later in the code if an unexpected field shows up or cause you to go modify that object's class for some reason that is unrelated to what that object does. - Consider
COALESCE
instead ofIFNULL
for determining conditional values (this is more suitable function in that it does not have potentially hidden value typing behavior). This is probably more common as well amongst MySQL developer types. - Write your query in a more readable form (not a series of concatenations, drop unnecessary parenthesis, ne consistent around using backticks, and capitalization of reserved words, etc.)
For example:
SELECT
direct_message.{field} AS {field_alias},
{other fields you need} AS {field_aliases},
COALESCE (direct_message_group.staff_id, 0) AS staff_id
FROM direct_message
LEFT JOIN direct_message_group
ON direct_message_group.chat_id = direct_message.id
INNER JOIN direct_message_thread
ON direct_message_thread.chat_id = direct_message.id
WHERE
(
direct_message.recipient_id = ?
OR direct_message.creator_id = ?
OR (
direct_message_group.staff_id = ?
AND direct_message_group.active = '0'
)
)
AND direct_message.school_id = ?
AND direct_message_thread.school_id = direct_message.school_id
GROUP BY direct_message.id
ORDER BY direct_message_thread.inserted DESC
LIMIT ?, ?
Here I have applied named parameters instead of you string concatenation.
- You query makes you relationship between tables seem unclear. On one hand you join
direct_message
todirect_message_thread
on some sort of chat id value, yet you also put in a WHERE condition that school id's for these records on these tables must be equal. Should this just be a part of the join condition as opposed to WHERE clause filter? addslashes()
may not be appropriate for escape purposes and it is not being used to escape$start
(offset value). I would suggest you either use prepared statements or an appropriate DB-connector-specific escape functionality. Also, if you know the values of your class methods calls are going to be integers, for example, do you really need to escape these?- You object instantiation for
direct_message
class seems weird. Why not pass the row data upon instantiation rather than having to call separate_populate()
method with the data? Why start that method name with an underscore? Consider UpperFirstCamelCase for all class/interface names instead of snake_case.
answered Jul 19, 2017 at 14:58
-
1\$\begingroup\$ It's worth noting that your example uses bound parameters with the same name. This can cause errors in some backends: stackoverflow.com/questions/18511645/… Also, bound parameters on the LIMIT clause only works when using emulated queries (which isn't usually the recommendation), and can cause SQL errors in some edge cases: stackoverflow.com/questions/2269840/… \$\endgroup\$Conor Mancone– Conor Mancone2017年07月19日 16:13:00 +00:00Commented Jul 19, 2017 at 16:13
-
1\$\begingroup\$ @ConorMancone With regard to emulated prepares and
LIMIT
, I believe it is actually the opposite of what you state, that when emulating prepared statements with PDO you have to explicitly bind integer value to parameter being used in limit clause. Either way, I have just gone ahead and edited my answer to use?
to remove ambiguity. Thanks for comment. \$\endgroup\$Mike Brant– Mike Brant2017年07月19日 17:58:03 +00:00Commented Jul 19, 2017 at 17:58 -
\$\begingroup\$ Regarding the limit, you're right, I got those backwards. Thanks! \$\endgroup\$Conor Mancone– Conor Mancone2017年07月19日 18:32:09 +00:00Commented Jul 19, 2017 at 18:32
default
addslashes
is not really the best way of preventing SQL injection? You will be much better off using prepared statements. There are some real world cases where addslashes will fail, see this: stackoverflow.com/questions/860954/…. Also, you should know that with MySQL you can't index anOR
condition. Fixing this may involve reworking large parts of your application to really fix it. Are you ready for that? \$\endgroup\$OR
conditions? \$\endgroup\$