Hibernate HQLなどのステートメントテンプレートを使用する方法はありますか?
SQLQuery sql=s.createSQLQuery("SELECT AVG(RATING) as r, COUNT(*) as c FROM RATINGS WHERE ADVENTURE_ID = ?");
sql.setParameter(0, adventureId);
または、ORMについて話していなければ、PDOが好きですか?
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
文字列を連結したり、注入されたデータを手動で引用したりしたくないので、2017年は残念です。
$db = JFactory::getDbo();
$db->setQuery('SELECT params FROM #__extensions WHERE name = ' . $db->quote('com_democompupdate'));
さらに悪いことに
$query->select($db->quoteName(array('user_id', 'profile_key', 'profile_value', 'ordering')));
$query->from($db->quoteName('#__user_profiles'));
$query->where($db->quoteName('profile_key') . ' LIKE '. $db->quote('\'custom.%\''));
$query->order('ordering ASC');
このプロジェクトでは本当にORMは必要ありません。dbは永久にMySQLになります。接続を取得し、トランザクションを開始し、ステートメントテンプレートを実行する方法が必要です。
新しいPDO接続を作成したくないので、JDatabase接続で作成したいことに注意してください。コンポーネントにdbアカウントの情報を知らせたくありません。
$query->bind
メソッドなので、準備済みステートメント、またはそれを呼び出すものを作成できます。しかし、私の場合、これはそれほど単純ではありません。私はバージョン3.4.3を持っています。私はサイトのメンテナーではないので、構成を変更したり、新しいバージョンに移行したりしたくありません。私は本番サーバーで実験したくないし、PHPの開発環境も持っていない。メモ帳だけだ。これは、私が小さなコンポーネントでのみ作業しており、PHPStormのようなIDEを購入して、これを最大5日間のプロジェクトで終わらせたくないためです。私もプロダクションでテストしています。これは完璧ではないことを理解しています...
設定で私はそれを見つけました$dbtype = 'mysqli'
。私の知る限り。 mysqliは準備されたステートメントをサポートします もですが、必ずしもmysqliドライバーがサポートすることを意味するわけではありません。
2014年の初期のJoomla 3.xでは、SQLiteとOracleにのみPDOベースのドライバーがあり、他のドライバーは準備済みステートメントをサポートしていませんでした。
Joomlaのバージョンが2015年のものであることがわかりました。この機能 2016年にmysqliドライバーに追加されました 。簡単なQueryTemplateクラスを作成しました。これを回避策として使用します。可能な場合は新しいバージョンに移行し、本番環境でコードをテストしないでください。 :D:D:D
私のバージョンはトランザクションをサポートしていることがわかりました。少なくともそれは大丈夫です。 joomlaクラスをラップすることにしましたが、これは必須ではありません。テンプレートはそれなしで使用できます。
JoomlaQueryTemplate.php:
namespace Canteen\infrastructure;
use Canteen\infrastructure\iTemplate;
use JFactory;
use Exception;
class JoomlaQueryTemplate implements iTemplate {
public function __construct($template){
if (!is_string($template))
throw new Exception('Invalid SQL template.');
$this->template = $template;
}
public function evaluate($data){
return preg_replace_callback('/:(\w+)/usD', function ($match) use ($data) {
$param = $match[1];
if (!array_key_exists($param, $data))
throw new Exception('Not given param: '.$param);
$value = JFactory::getDbo()->quote($data[$param]);
return $value;
}, $this->template);
}
}
JoomlaConnection.php
namespace Canteen\infrastructure;
use Canteen\infrastructure\JoomlaQueryTemplate;
use JFactory;
class JoomlaConnection implements iConnection {
protected $connection;
public function __construct(){
$this->connection = JFactory::getDbo();
}
public function execute($template, $data = array()){
$queryTemplate = new JoomlaQueryTemplate($template);
$query = $queryTemplate->evaluate($data);
$this->connection->setQuery($query);
$this->connection->execute();
}
public function getId(){
return $this->connection->insertid();
}
public function query($template, $data = array()){
$queryTemplate = new JoomlaQueryTemplate($template);
$query = $queryTemplate->evaluate($data);
$this->connection->setQuery($query);
$this->connection->execute();
}
public function isEmpty(){
$rowsCount = $this->connection->getNumRows();
return $rowsCount == 0;
}
public function getMany(){
return $this->connection->loadObjectList();
}
public function getOne(){
return $this->connection->loadObject();
}
public function getValues(){
return $this->connection->loadColumn();
}
public function getValue(){
return $this->connection->loadResult();
}
public function beginTransaction(){
$this->connection->transactionStart();
}
public function commit(){
$this->connection->transactionCommit();
}
public function rollback(){
$this->connection->transactionRollback();
}
}
接続は私のリポジトリとアプリサービスに挿入されます。そのため、アプリサービスはトランザクションを処理でき、リポジトリはSQLクエリを送信できます。例えば:
public function readStatistics(){
$statisticsDTO = new CustomerStatisticsDTO();
try {
$this->connection->beginTransaction();
$statisticsDTO->setTotalCount($this->repository->countCustomers());
$statisticsDTO->setActiveCount($this->repository->countActiveCustomers());
$statisticsDTO->setSuspendedCount($this->repository->countSuspendedCustomers());
$this->connection->commit();
}
catch (Exception $exception){
$this->connection->rollback();
throw $exception;
}
$statisticsDTO->setPassiveCount($statisticsDTO->getTotalCount() - $statisticsDTO->getActiveCount());
$statisticsDTO->setOrderingCount($statisticsDTO->getActiveCount() - $statisticsDTO->getSuspendedCount());
return $statisticsDTO;
}
そして
public function countSuspendedCustomers(){
$today = new DateTime('today');
$this->connection->query(
'SELECT COUNT(`#__canteen_customers`.`user_id`) AS `result` '.
'FROM `#__canteen_customers` '.
'WHERE '.
'0 < ('.
'SELECT count(`#__canteen_suspensions`.`suspension_id`) AS `active_suspension_count` '.
'FROM `#__canteen_suspensions` '.
'WHERE '.
'`#__canteen_customers`.`user_id` = `#__canteen_suspensions`.`user_id` AND '.
'`#__canteen_suspensions`.`suspension_from` <= :date AND '.
'(`#__canteen_suspensions`.`suspension_to` IS NULL OR `#__canteen_suspensions`.`suspension_to` >= :date)'.
') AND '.
'`#__canteen_customers`.`customer_active` = TRUE',
array(
'date' => $today->format('Y-m-d')
)
);
return (int) $this->connection->getValue();
}
(selectステートメントのみでロールバックする必要がないことはわかっていますが、コードのその部分をコピーして貼り付けただけで、害はありません。作業単位の方が良いと思いますが、私は学習していますそのパターンは後で。)
元のjoomlaクエリビルダーAPIも良いと確信していますが、クエリビルダーコードと結果のSQLとの間の変換方法を学ぶ必要がなかったので、準備されたステートメントを使用するほうが自然に感じました。たとえば、後でpgsqlを使用したくないので、生成する代わりにsqlを記述してもかまいません。よく選択できる場合は、準備済みステートメントをサポートする最新のjoomlaバージョンをインストールし、これの代わりにそれを使用します。