Driver: Mysqli
3rt party: Kunena 6.0 alpha 3 nightly
Forum Message has 4 attachments.
C:\Users\Jelle\Documents\gitkraken\Kunena-Forum\src\libraries\kunena\attachment\helper.php:237:
SELECT * FROM
#__kunena_attachmentsWHERE
mesid IN (67)' (length=63)
C:\Users\Jelle\Documents\gitkraken\Kunena-Forum\src\libraries\kunena\attachment\helper.php:261: array (size=1) 245 => object(KunenaAttachment)[1933]
Code:
$idlist = implode(',', $ids);
$db = Factory::getDBO();
$query = $db->getQuery(true);
$query->clear()
->select('*')
->from($db->quoteName('#__kunena_attachments'))
->where($db->quoteName('mesid') . ' IN (' . $idlist . ')');
$db->setQuery((string) $query);
try
{
$results = (array) $db->loadObjectList('id', 'KunenaAttachment');
}
catch (RuntimeException $e)
{
KunenaError::displayDatabaseError($e);
}
Loaded 4 Objects
Just 1.
With J3.8 i see 4 objects.
Labels |
Added:
?
|
Labels |
Added:
J4 Issue
|
Status | New | ⇒ | Discussion |
something is wrong on your side i've tested with this silly snippet
$query->clear()
->select('*')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('package_id') . ' IN (802)');
$db->setQuery($query);
$results = $db->loadObjectList('extension_id');
and results have correctly the 2 instances of English (en-GB)
client and admin corresponding to 802 as expected
Status | Discussion | ⇒ | Closed |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2019-05-11 09:57:14 |
Closed_By | ⇒ | alikon | |
Labels |
Closed_By | alikon | ⇒ | joomla-cms-bot |
Labels |
Set to "closed" on behalf of @alikon by The JTracker Application at issues.joomla.org/joomla-cms/24857
stdClass shouldn't be enough ?
Not for us, we have our own classes.
stdClass shouldn't be enough ?
In this case it should have KunenaAttachment object not stdClass, in previous Joomla! 3.9.x and before the method loadObjectList('id', 'KunenaAttachment') fetch the object KunenaAttachment for each row find with the query. Joomla! 4.0 doesn't behave like that, it fetch the object KunenaAttachment of first row and ignore the others rows.
This should still be the case see the src
https://github.com/joomla-framework/database/blob/2.0-dev/src/DatabaseDriver.php#L1308
By calling $db->loadObjectList('id');
it show as result :
array (size=2)
11 =>
object(stdClass)[924]
public 'id' => int 11
public 'mesid' => int 43
public 'userid' => int 106
public 'protected' => int 0
public 'hash' => string '68ed90e0ef18f2c9414f1160d492987f' (length=32)
public 'size' => int 146235
public 'folder' => string 'media/kunena/attachments/106' (length=28)
public 'filetype' => string 'image/jpeg' (length=10)
public 'filename' => string 'IMG_20190601_115057_2019-06-03.jpg' (length=34)
public 'filename_real' => string 'IMG_20190601_115057.jpg' (length=23)
public 'caption' => string '' (length=0)
public 'inline' => int 0
12 =>
object(stdClass)[916]
public 'id' => int 12
public 'mesid' => int 43
public 'userid' => int 106
public 'protected' => int 0
public 'hash' => string 'ce2f0eda84fa846a02df5977fca59c81' (length=32)
public 'size' => int 174101
public 'folder' => string 'media/kunena/attachments/106' (length=28)
public 'filetype' => string 'image/jpeg' (length=10)
public 'filename' => string 'IMG_20190601_115207.jpg' (length=23)
public 'filename_real' => string 'IMG_20190601_115207.jpg' (length=23)
public 'caption' => string '' (length=0)
public 'inline' => int 0
By calling $db->loadObjectList('id', 'KunenaAttachment');
it show as result :
array (size=1)
11 =>
object(KunenaAttachment)[924]
public 'id' => int 11
public 'disabled' => boolean false
protected '_table' => string 'KunenaAttachments' (length=17)
protected 'path' => null
protected 'width' => null
protected 'height' => null
protected 'shortname' => null
public 'folder' => string 'media/kunena/attachments/106' (length=28)
public 'userid' => int 106
public 'mesid' => int 43
public 'protected' => int 0
public 'hash' => string '68ed90e0ef18f2c9414f1160d492987f' (length=32)
public 'size' => int 146235
public 'filetype' => string 'image/jpeg' (length=10)
public 'filename' => string 'IMG_20190601_115057_2019-06-03.jpg' (length=34)
public 'filename_real' => string 'IMG_20190601_115057.jpg' (length=23)
public 'comment' => null
public 'inline' => int 0
public 'typeAlias' => null
public 'caption' => string '' (length=0)
protected '_name' => string 'KunenaAttachment' (length=16)
protected '_exists' => boolean false
protected '_saving' => boolean false
protected '_errors' =>
array (size=0)
empty
I'm going to take a guess you're using MySQLi. And I'm going to take a guess the custom class you're loading has code in its constructor which results in a database query being executed in select circumstances, and as a result the result cursor from your multi-row query is being lost. Not much that can be done here without dropping prepared statement support or dropping MySQLi support.
The long and short of it is the way things are structured right now is the way things have to be in order to support MySQL compiled with libmysql and not mysqlnd. This diff from Doctrine's DBAL covers part of the problem. As a result of this structure, mysqli_fetch_object($cursor, $class);
cannot be called as is the case in 3.x; to emulate this behavior, the MysqliStatement
class instantiates your custom class then sets all the properties after instantiation. Note that PDO has quirks in its fetch object behavior as well where the data is set to an object before the constructor is actually executed.
So, relying on PDOStatement::fetchObject()
or mysqli_result::fetch_object()
to correctly instantiate your custom objects is a bit hit or miss, especially when using prepared statements in the case of MySQLi (which 3.x does not support at all) and you're better off using a data mapping layer to convert the query result into your selected PHP class after the results are pulled.
Sorry for commenting on this old issue but it's the only reference on this weird error you can find online.
Saying that it's the developer's fault for providing a faulty class which requires initialisation is obviously wrong.
The problem really happens because the Joomla\Database\Mysqli\MysqliStatement
class doesn't implement FetchMode::CUSTOM_OBJECT
at all. The fetch
method of that class is what throws the error.
This means that you can't reliably use the Joomla database driver's loadObjectList
method with a custom class name because whether it works or not is up to the database driver being used.
This should be fixed in the upstream database package either by implementing this fetch mode in the MySQLi driver's MySQLiStatement class or by removing the inconsistent support for custom class names altogether. Unless, of course, Joomla decides on a hard PDO dependency in which case mapping the MySQLi driver to the PDO driver would work just fine but Joomla wouldn't run on a non-insignificant proportion of live and most prepackaged local hosts due to lack of default PDO support in PHP.
In the meantime, you can replace this one line code which runs great on Joomla 3:
$results = $db->loadObjectList('', $myClassName) ?: [];
with this monstrosity on Joomla 4:
$results = array_map(function (array $arr) use ($myClassName) {
$o = new $myClassName;
foreach ($arr as $k => $v)
{
$o->{$k} = $v;
}
return $o;
}, $db->loadAssocList() ?: []);
For starters...
If you're calling
$db->getQuery(true);
, your first method call doesn't need to be$query->clear()
because you're already dealing with a new DatabaseQuery object.$db->setQuery((string) $query);
is just wasting CPU cycles, on both 3.x and 4.0. On 3.x, the PDO drivers are converting string queries into query objects for prepared statement support (not as big of a deal since very few actually use the PDO drivers), the non-PDO drivers will handle converting to string when the time is right; in 4.0 everything is internally standardized into query objects otherwise prepared statements don't work at all. So you're just unwrapping a query object to wrap it back into a new query object.A test case in https://github.com/joomla-framework/database/tree/2.0-dev for whatever drivers are affected would be beneficial as I highly doubt if there is some kind of bug that it affects 5 different drivers with 3 different code paths (MySQLi, SQL Server, and 3 PDO drivers). A code snippet with a random query from a third party extension without a database schema to work with isn't going to go very far, especially as changes in code should be accompanied by automated tests.