\documentclass{article}
\usepackage{ulem}
\usepackage{graphicx}
\usepackage{hyperref}
\pagestyle{headings}
\begin{document}
\part{Rdo - Rampage Data Objects}
Rdo is a DataMapper implementation for Horde, using domain/entity objects combined with Mapper objects that handle the actual data manipulation.

\section{Bugs}
Rdo currently gives unexpected results for to-one relations where no or a wrong relation key exists. [\#10880]

\section{Ideas for H6}
\subsection{Rethink responsibilities between db and rdo}
\begin{itemize}
\item <a href="https://wiki.horde.org/QueryBuilder">QueryBuilder</a> makes sense independent of Rdo.


\item DB has hardcoded object creation for Horde\_Date from some field types. This is basically proto-mapping. Should DB really do that and in this way?


\end{itemize}
\subsection{Allow subtyping or wrapping the Horde\_Rdo\_Base entities}
\begin{itemize}
\item All parts of the Rdo framework delegate the actual instanciation of entities to Horde\_Rdo\_Mapper::map()<br />
The map() method can be overwritten by the implementing mapper or an intermediate app-specific base class<br />
You may even do away with Horde\_Rdo\_Base entities completely, but then you will want to patch create(), update() and delete, too<br />
If you use Horde\_Rdo\_Factory, you may want to subclass it and overwrite the create() method.<br />
Most likely you will want to inject some dependencies to your entities.


\end{itemize}
\subsection{Refactor against interfaces rather than base class}
\subsection{Have a minimum interface which allows "plain" entity objects}
\begin{itemize}
\item without the smart delete, update etc shortcuts.


\item Or really just arrays or <a href="https://wiki.horde.org/PoPos">PoPos</a>.


\end{itemize}
\subsection{Move some of the rarely used stuff to traits. Keep the traits in the default Horde\_Rdo\_Base but don't require them in the base interface}
\subsection{Maybe some more dynamic approach to lazy loading?}
\subsection{\_\_toString() for Horde\_Rdo\_Query objects. Basically a convenience wrapper for ->getQuery().}
\subsection{returning exceptions instead of null for wrong relation keys or null relation keys. Facilitates chaining relations like \$baby->mother->granny->name. Currently if granny isn't present, we run into a php fatal (method on non-object). This is a BC break.}
\section{People}
<a href="https://wiki.horde.org/ChuckHagenbuch">ChuckHagenbuch</a> is the primary author of Rdo.

\section{Description}
\subsection{Notes on database abstraction}
MDB2\_Schema xslt:<br />
<a href="http://cvs.php.net/viewvc.cgi/pear/MDB2\_Schema/docs/">http://cvs.php.net/viewvc.cgi/pear/MDB2\textbackslash\{\}\_Schema/docs/</a>

regarding charset support, here's a topic summary:

<a href="http://www.alberton.info/dbms\_charset\_settings\_explained.html">http://www.alberton.info/dbms\textbackslash\{\}\_charset\textbackslash\{\}\_settings\textbackslash\{\}\_explained.html</a><br />
<a href="http://oss.backendmedia.com/MDB2/[CharacterSet](CharacterSet)">http://oss.backendmedia.com/MDB2/[CharacterSet](CharacterSet)</a><br />
<a href="http://pear.php.net/bugs/4666">http://pear.php.net/bugs/4666</a>

In MDB2, most drivers basically resort to a "SET NAMES" query.<br />
The <a href="https://wiki.horde.org/MySQL">MySQL</a> driver initially used the "SET character\_set\_client"<br />
statement, but I changed it after some complaints, for it<br />
was not effective for most setups.

Another thing that bothers me, is the fact that<br />
there isn't a single charset naming convention.

<a href="http://www.postgresql.org/docs/8.2/interactive/multibyte.html#MULTIBYTE-CHARSET-SUPPORTED">http://www.postgresql.org/docs/8.2/interactive/multibyte.html\#MULTIBYTE-CHARSET-SUPPORTED</a><br />
<a href="http://dev.mysql.com/doc/refman/5.0/en/charset-charsets.html">http://dev.mysql.com/doc/refman/5.0/en/charset-charsets.html</a><br />
<a href="http://www.destructor.de/firebird/charsets.htm">http://www.destructor.de/firebird/charsets.htm</a><br />
<a href="http://www.csee.umbc.edu/help/oracle8/server.815/a67789/appa.htm#956722">http://www.csee.umbc.edu/help/oracle8/server.815/a67789/appa.htm\#956722</a>

For instance, Traditional Chinese is labelled as "BIG\_5",<br />
"BIG5" or "<a href="https://wiki.horde.org/ZHT16BIG5">ZHT16BIG5</a>", Russian as "KOI8", "<a href="https://wiki.horde.org/KOI8R">KOI8R</a>" or "<a href="https://wiki.horde.org/CL8KOI8R">CL8KOI8R</a>",<br />
Western European as "latin1", "ISO8859\_1" or "<a href="https://wiki.horde.org/WE8ISO8859P1">WE8ISO8859P1</a>"...<br />
In the end, a common setCharset() method is kind of pointless<br />
without a standard charset naming convention...<br />
Should the DBAL define a set of constants for the most<br />
common charsets, and leave the mapping to the drivers?

"LIKE" case sensitivity:

\begin{quote}
I was not thinking of SQLite. I was thinking that <a href="https://wiki.horde.org/MySQL">MySQL</a> LIKE operator is<br />
case insensitive (which is what most people want) but in other databases<br />
LIKE is case sensitive by default.


\end{quote}
Not sure how you did in Metabase, but in MDB2 I have the following code for this:<br />
switch (\$operator) \{<br />
* case insensitive<br />
case 'ILIKE':<br />
\$match = \$field.'LIKE ';<br />
break;<br />
* case sensitive<br />
case 'LIKE':<br />
\$match = \$field.'LIKE BINARY ';<br />
break;

Not sure if I would use "ILIKE" again, probably I would use like with an optional<br />
attribute to flag case sensitivity.

\subsection{Logging what Rdo does}
See "ror\_logs.png" attachment.

\subsection{Database field sizing}
When possible, make varchar fields 2**n - 1 (255, etc). Internally (<a href="https://wiki.horde.org/MySQL">MySQL</a>), one extra byte is needed to store the length of the string. So when you add the extra byte, the length is then an even power of two. This is desirable for technical reasons. In particular, it ensures that the field will be on an even word boundary given that eight byte words are used.

Obviously, an exception is when the data in a field is inherently of a particular length. For example, <a href="https://wiki.horde.org/NYAuthUser">NYAuthUser</a>.<a href="https://wiki.horde.org/UserPass">UserPass</a> contains an MD5 hash of the actual password. md5() always returns exactly 32 characters. So the field should indeed be exactly 32 characters long - and thus a CHAR instead of a VARCHAR. CHAR fields don't need the extra byte.

\subsection{Paging}
You hardly need a library for figuring out pagination. A very simple formula tells you how many "pages" you have.

<pre><code class="language-php">
\$itemsPerPage = 10;
\$totalRecords = 52; //Result of SELECT FOUND\_ROWS()
\$pages = ceil(\$totalRecords/\$itemsPerPage);
</code></pre>
Based on those numbers, you know you have 6 pages of data. You can then create a loop to generate the links with the proper parameter(s), however you want to format them. For example:

<pre><code>
link.php?page=3\&itemsperpage=10
</code></pre>
Would make the following query:

<pre><code>
SELECT * FROM table LIMIT (\$\_GET['page']-1)*10,10
</code></pre>
I may be off by 1 on that. Hope that helps.

On 10/18/07, Brent Baisley <<a href="https://wiki.horde.org/mailto:brenttech@gmail.com">brenttech@gmail.com</a>> wrote:

You don't need to do an extra count query. If you are using <a href="https://wiki.horde.org/MySQL">MySQL</a>, just add SQL\_CALC\_FOUND\_ROWS to your select query. SELECT SQL\_CALC\_FOUND\_ROWS * FROM ...

You can then run SELECT FOUND\_ROWS() to get the total rows without any limits. It's still 2 queries, but the second one is essentially free.

\subsection{Adding backup database servers}
\begin{quote}
I'm kind of of the opinion that this sort of thing (failover) belongs on the database server, not in the app.


\end{quote}
In our environment, the apps that we use all utilize this sort of thing for a rudimentary failover behaviour (postfix, amavisd, etc). We modified courier-authlib and dovecot to include this type of behaviour.  We can handle a complete crash of our primary r/w db server and still keep our imap/http/pop3/smtp services operational.

We have two database servers that are circular redundant -> mysql-01 and mysql-02.  mysql-01 is the primary r/w server.  If an app cannot connecto mysql-01, then I would like to to connec to to mysql-02 and process as normal.  When mysql-01 comes back online, it reads the updates from -02 and continues normal operation.  We also have a handful of r/o slave servers for the mx servers to query for user metadata.

How would you suggest the database server handle this instead of the app?  Obviously if the db server is down, it won't be able to process a message to redirect the host to the next server...

Horde is the only app that we have that doesn't include some sort of failover (on connect).  I quick whipped up a patch if you think it might be usefull, otherwise we will just use it here.  I have tested it and it works sufficent for our needs.  We have had to modify the framework, horde, jonah, kronolith, and turba.  The modifications are minor to each file and I have attached them.  All changes are off of CVS HEAD.

See "Re\_ \_horde\_ adding backup servers to mysql db driver.zip" attachment.

\section{Resources}
<a href="http://ajaxian.com/archives/ext-scaffold-generator-plugin-for-rails">http://ajaxian.com/archives/ext-scaffold-generator-plugin-for-rails</a><br />
<a href="http://ajaxian.com/archives/lipsiadmin-rails-20-ext-admin">http://ajaxian.com/archives/lipsiadmin-rails-20-ext-admin</a><br />
<a href="http://blog.stuartherbert.com/php/2008/05/08/what-should-an-orm-offer/">http://blog.stuartherbert.com/php/2008/05/08/what-should-an-orm-offer/</a><br />
<a href="http://code.nytimes.com/projects/dbslayer">http://code.nytimes.com/projects/dbslayer</a><br />
<a href="http://www.phpdoctrine.org/index.php/documentation/manual?chapter=migration">http://www.phpdoctrine.org/index.php/documentation/manual?chapter=migration</a><br />
<a href="http://michaelkimsal.com/blog/gorm-goodness-in-php/">http://michaelkimsal.com/blog/gorm-goodness-in-php/</a><br />
<a href="http://grails.org/GORM">http://grails.org/GORM</a><br />
<a href="http://jan.kneschke.de/2007/2/19/typesafe-objects-in-php">http://jan.kneschke.de/2007/2/19/typesafe-objects-in-php</a><br />
<a href="http://jeremy.zawodny.com/mysql/mysql-optimization.html">http://jeremy.zawodny.com/mysql/mysql-optimization.html</a><br />
<a href="http://kore-nordmann.de/blog/why\_active\_record\_sucks.html">http://kore-nordmann.de/blog/why\textbackslash\{\}\_active\textbackslash\{\}\_record\textbackslash\{\}\_sucks.html</a><br />
<a href="http://code.google.com/p/pdorm/">http://code.google.com/p/pdorm/</a><br />
<a href="http://phing.info/docs/guide/current/chapters/appendixes/AppendixC-OptionalTasks.html#[DbDeployTask](DbDeployTask)">http://phing.info/docs/guide/current/chapters/appendixes/AppendixC-OptionalTasks.html\#[DbDeployTask](DbDeployTask)</a><br />
<a href="http://pooteeweet.org/blog/0/918#m918">http://pooteeweet.org/blog/0/918\#m918</a><br />
<a href="http://pooteeweet.org/blog/0/1083#m1083">http://pooteeweet.org/blog/0/1083\#m1083</a><br />
<a href="http://sebastian-bergmann.de/archives/751-Testing-with-SQLite-In-Memory-Databases.html">http://sebastian-bergmann.de/archives/751-Testing-with-SQLite-In-Memory-Databases.html</a><br />
<a href="http://www.blobstreaming.org/">http://www.blobstreaming.org/</a><br />
<a href="http://www.ddataobjects.com/current/prog/">http://www.ddataobjects.com/current/prog/</a><br />
<a href="http://www.phpdoctrine.org/documentation/manual?chapter=migration">http://www.phpdoctrine.org/documentation/manual?chapter=migration</a><br />
<a href="http://framework.zend.com/manual/en/zend.db.profiler.html">http://framework.zend.com/manual/en/zend.db.profiler.html</a><br />
<a href="https://martinfowler.com/eaaCatalog/dataMapper.html">https://martinfowler.com/eaaCatalog/dataMapper.html</a>


\noindent\rule{\textwidth}{1pt}
Back to the <a href="https://wiki.horde.org/Project">Project List</a>

\end{document}
