PageRenderTime 64ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/en/reference/sdodasrel/examples.xml

https://github.com/ifeghali/phpdocs
XML | 1161 lines | 1034 code | 106 blank | 21 comment | 0 complexity | 53b3ab71def18b1eef05fe18901a6253 MD5 | raw file
Possible License(s): CC-BY-3.0
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- $Revision: 1.4 $ -->
  3. <chapter xml:id="sdo.das.rel.examples" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
  4. &reftitle.examples;
  5. <section xml:id='sdo.das.rel.examples-crud'>
  6. <title>Creating, retrieving, updating and deleting data</title>
  7. <para>
  8. This section illustrates how the Relational DAS can be used to create,
  9. retrieve, update and delete data in a relational database.
  10. Many of the examples are illustrated with a three-table database that
  11. contains companies, departments within those companies, and employees
  12. that work in those departments. This example is used in a number of
  13. places within the SDO literature. See the examples section of the
  14. <link xlink:href='&url.ibm.sdo.spec;'>Service Data Objects specification</link>
  15. or the
  16. <link linkend="sdo.examples">Examples</link>
  17. section of the documentation for the SDO extension.
  18. </para>
  19. <para>
  20. The Relational DAS is constructed with metadata that defines the
  21. relational database and how it should be mapped to SDO.
  22. The long section that follows describes this metadata and how to
  23. construct the Relational DAS. The examples that follow it all assume that
  24. this metadata is in an included php file.
  25. </para>
  26. <para>
  27. The examples below and others can all be found in the
  28. <filename>Scenarios</filename>
  29. directory in the Relational DAS package.
  30. </para>
  31. <para>
  32. The Relational DAS throws exceptions in the event that it finds errors
  33. in the metadata or errors when executing SQL statements against the
  34. database. For brevity the examples below all omit the use of try/catch
  35. blocks around the calls to the Relational DAS.
  36. </para>
  37. <para>
  38. These examples all differ from the expected use of SDO in two
  39. important respects.
  40. </para>
  41. <para>
  42. First, they show all interactions with the database completed within
  43. one script. In this respect these scenarios are not realistic but are
  44. chosen to illustrate just the use of the Relational DAS.
  45. It is expected that interactions with the database will be separated
  46. in time and the data graph serialized and deserialized into the PHP
  47. session one or more times as the application interacts with an end user.
  48. </para>
  49. <para>
  50. Second, all queries executed against the database use hard-coded
  51. queries with no variables substituted. In this case it is safe to
  52. use the simple
  53. <function>executeQuery</function>
  54. call, and this is what the examples illustrate.
  55. In practice, though, it is unlikely that the SQL statement is known
  56. entirely ahead of time. In order to allow variables to be safely
  57. substituted into the SQL queries, without running the risk of
  58. injecting SQL with unknown effects, it is safer to use the
  59. <function>executePreparedQuery</function>
  60. which takes a prepared SQL statement containing placeholders
  61. and a list of values to be substituted.
  62. </para>
  63. </section>
  64. <section xml:id='sdo.das.rel.metadata'>
  65. <title>Specifying the metadata</title>
  66. <para>
  67. This first long section describes in detail how the metadata describing
  68. the database and the required SDO model is supplied to the
  69. Relational DAS.
  70. </para>
  71. <para>
  72. When the constructor for the Relational DAS is invoked, it needs to be
  73. passed several pieces of information. The bulk of the information,
  74. passed as an associative array in the first argument to the constructor,
  75. tells the Relational DAS what it needs to know about the relational
  76. database. It describes the names of the tables, columns, primary keys
  77. and foreign keys. It should be fairly easy to understand what is
  78. required, and once written it can be placed in a php file and included
  79. when needed. The remainder of the information, passed in the second
  80. and third arguments to the constructor, tells the Relational DAS what
  81. it needs to know about the relationships between objects and the shape
  82. of the data graph; it ultimately determines how the data from the
  83. database is to be normalized into a graph.
  84. </para>
  85. <section xml:id='sdo.das.rel.metadata.database'>
  86. <title>Database metadata</title>
  87. <para>
  88. The first argument to the constructor describes the target
  89. relational database.
  90. </para>
  91. <para>
  92. Each table is described by an associative array with up to four keys.
  93. <informaltable>
  94. <tgroup cols='2'>
  95. <thead>
  96. <row>
  97. <entry>Key</entry>
  98. <entry>Value</entry>
  99. </row>
  100. </thead>
  101. <tbody>
  102. <row>
  103. <entry>name</entry>
  104. <entry>The name of the table.</entry>
  105. </row>
  106. <row>
  107. <entry>columns</entry>
  108. <entry>
  109. An array listing the names of the columns, in any order.
  110. </entry>
  111. </row>
  112. <row>
  113. <entry>PK</entry>
  114. <entry>The name of the column containing the primary key.</entry>
  115. </row>
  116. <row>
  117. <entry>FK</entry>
  118. <entry>An array with two entries, 'from' and 'to', which define
  119. a column containing a foreign key, and a table to which the foreign
  120. key points. If there are no foreign keys in the table then the
  121. 'FK' entry does not need to be specified. Only one foreign key
  122. can be specified. Only a foreign key pointing to the primary key
  123. of a table can be specified.
  124. </entry>
  125. </row>
  126. </tbody>
  127. </tgroup>
  128. </informaltable>
  129. </para>
  130. <programlisting role="php">
  131. <![CDATA[
  132. <?php
  133. /*****************************************************************
  134. * METADATA DEFINING THE DATABASE
  135. ******************************************************************/
  136. $company_table = array (
  137. 'name' => 'company',
  138. 'columns' => array('id', 'name', 'employee_of_the_month'),
  139. 'PK' => 'id',
  140. 'FK' => array (
  141. 'from' => 'employee_of_the_month',
  142. 'to' => 'employee',
  143. ),
  144. );
  145. $department_table = array (
  146. 'name' => 'department',
  147. 'columns' => array('id', 'name', 'location', 'number', 'co_id'),
  148. 'PK' => 'id',
  149. 'FK' => array (
  150. 'from' => 'co_id',
  151. 'to' => 'company',
  152. )
  153. );
  154. $employee_table = array (
  155. 'name' => 'employee',
  156. 'columns' => array('id', 'name', 'SN', 'manager', 'dept_id'),
  157. 'PK' => 'id',
  158. 'FK' => array (
  159. 'from' => 'dept_id',
  160. 'to' => 'department',
  161. )
  162. );
  163. $database_metadata = array($company_table, $department_table, $employee_table);
  164. ?>
  165. ]]>
  166. </programlisting>
  167. <para>
  168. This metadata corresponds to a relational database that might have
  169. been defined to MySQL as:
  170. </para>
  171. <programlisting role="sql">
  172. <![CDATA[
  173. create table company (
  174. id integer auto_increment,
  175. name char(20),
  176. employee_of_the_month integer,
  177. primary key(id)
  178. );
  179. create table department (
  180. id integer auto_increment,
  181. name char(20),
  182. location char(10),
  183. number integer(3),
  184. co_id integer,
  185. primary key(id)
  186. );
  187. create table employee (
  188. id integer auto_increment,
  189. name char(20),
  190. SN char(4),
  191. manager tinyint(1),
  192. dept_id integer,
  193. primary key(id)
  194. );
  195. ]]>
  196. </programlisting>
  197. <para>
  198. or to DB2 as:
  199. </para>
  200. <programlisting role="sql">
  201. <![CDATA[
  202. create table company ( \
  203. id integer not null generated by default as identity, \
  204. name varchar(20), \
  205. employee_of_the_month integer, \
  206. primary key(id) )
  207. create table department ( \
  208. id integer not null generated by default as identity, \
  209. name varchar(20), \
  210. location varchar(10), \
  211. number integer, \
  212. co_id integer, \
  213. primary key(id) )
  214. create table employee ( \
  215. id integer not null generated by default as identity, \
  216. name varchar(20), \
  217. SN char(4), \
  218. manager smallint, \
  219. dept_id integer, \
  220. primary key(id) )
  221. ]]>
  222. </programlisting>
  223. <para>
  224. Note that although in this example there are no foreign keys specified
  225. to the database and so the database is not expected to enforce
  226. referential integrity, the intention behind the
  227. <varname>co_id</varname>
  228. column on the department table and the
  229. <varname>dept_id</varname>
  230. column on the employee table is they should contain the primary key
  231. of their containing company or department record, respectively.
  232. So these two columns are acting as foreign keys.
  233. </para>
  234. <para>
  235. There is a third foreign key in this example, that from the
  236. <varname>employee_of_the_month</varname>
  237. column of the company record to a single row of the employee table.
  238. Note the difference in intent between this foreign key and the other
  239. two. The
  240. <varname>employee_of_the_month</varname>
  241. column represents a single-valued relationship: there can be only
  242. one employee of the month for a given company.
  243. The
  244. <varname>co_id</varname>
  245. and
  246. <varname>dept_id</varname>
  247. columns represent multi-valued relationships: a company can contain
  248. many departments and a department can contain many employees.
  249. This distinction will become evident when the remainder of the metadata
  250. picks out the company-department and department-employee relationships
  251. as containment relationships.
  252. </para>
  253. <para>
  254. There are a few simple rules to be followed when constructing the
  255. database metadata:
  256. </para>
  257. <itemizedlist>
  258. <listitem>
  259. <para>
  260. All tables must have primary keys, and the primary keys must be
  261. specified in the metadata. Without primary keys it is not possible
  262. to keep track of object identities. As you can see from the SQL
  263. statements that create the tables, primary keys can be
  264. auto-generated, that is, generated and assigned by the database when
  265. a record is inserted. In this case the auto-generated primary key
  266. is obtained from the database and inserted into the data object
  267. immediately after the row is inserted into the database.
  268. </para>
  269. </listitem>
  270. <listitem>
  271. <para>
  272. It is not necessary to specify in the metadata all the columns
  273. that exist in the database, only those that will be used.
  274. For example, if the company table had another column that the
  275. application did not want to access with SDO, this need not be
  276. specified in the metadata. On the other hand it would have done
  277. no harm to specify it: if specified in the metadata but never
  278. retrieved, or assigned to by the application, then the unused column
  279. will not affect anything.
  280. </para>
  281. </listitem>
  282. <listitem>
  283. <para>
  284. In the database metadata note that the foreign key definitions
  285. identify not the destination column in the table which is pointed
  286. to, but the table name itself. Strictly, the relational model
  287. permits the destination of a foreign key to be a non-primary key.
  288. Only foreign keys that point to a primary key are useful for
  289. constructing the SDO model, so the metadata specifies the table name.
  290. It is understood that the foreign key points to the primary key of
  291. the given table.
  292. </para>
  293. </listitem>
  294. </itemizedlist>
  295. <para>
  296. Given these rules, and given the SQL statements that define the
  297. database, the database metadata should be easy to construct.
  298. </para>
  299. <section xml:id='sdo.das.rel.metadata.database.model'>
  300. <title>What the Relational DAS does with the metadata</title>
  301. <para>
  302. The Relational DAS uses the database metadata to form most of the
  303. SDO model. For each table in the database metadata, an SDO type
  304. is defined. Each column which can represent a primitive value
  305. (columns which are not defined as foreign keys) are added
  306. as properties to the SDO type.
  307. </para>
  308. <para>
  309. All primitive properties are given a type of string in the SDO model,
  310. regardless of their SQL type. When writing values back to the
  311. database the Relational DAS will create SQL statements that treat
  312. the values as strings, and the database will convert them to the
  313. appropriate type.
  314. </para>
  315. <para>
  316. Foreign keys are interpreted in one of two ways, depending on the
  317. metadata in the third argument to the constructor that defines
  318. the SDO containment relationships.
  319. A discussion of this is therefore deferred until the section on
  320. <link linkend="sdo.das.rel.metadata.crels">
  321. SDO containment relationships
  322. </link>
  323. below.
  324. </para>
  325. </section>
  326. <section xml:id='sdo.das.rel.metadata.approottype'>
  327. <title>Specifying the application root type</title>
  328. <para>
  329. The second argument to the constructor is the application root type.
  330. The true root of each data graph is an object of a special root type
  331. and all application data objects come somewhere below that. Of the
  332. various application types in the SDO model, one has to be the
  333. application type immediately below the root of the data graph.
  334. If there is only one table in the database metadata, the application
  335. root type can be inferred, and this argument can be omitted.
  336. </para>
  337. </section>
  338. <section xml:id='sdo.das.rel.metadata.crels'>
  339. <title>Specifying the SDO containment relationships</title>
  340. <para>
  341. The third argument to the constructor defines how the types in the
  342. model are to be linked together to form a graph. It identifies the
  343. parent-child relationships between the types which collectively form a
  344. graph. The relationships need to be supported by foreign keys to be
  345. found in the data, in a way shortly to be described.
  346. </para>
  347. <para>
  348. The metadata is an array containing one or more associative arrays,
  349. each of which identifies a parent and a child. The example below shows
  350. a parent-child relationship from company to department, and another
  351. from department to employee. Each of these will become an SDO property
  352. defining a multi-valued containment relationship in the SDO model.
  353. </para>
  354. <programlisting role="php">
  355. <![CDATA[
  356. <?php
  357. $department_containment = array( 'parent' => 'company', 'child' => 'department');
  358. $employee_containment = array( 'parent' => 'department', 'child' => 'employee');
  359. $SDO_containment_metadata = array($department_containment, $employee_containment);
  360. ?>
  361. ]]>
  362. </programlisting>
  363. <para>
  364. Foreign keys in the database metadata are interpreted as properties
  365. with either multi-valued containment relationships or single-valued
  366. non-containment references, depending on whether they have a
  367. corresponding SDO containment relationship specified in the metadata.
  368. In the example here, the foreign keys from department to company (the
  369. <varname>co_id</varname>
  370. column in the department table)
  371. and from employee to department (the
  372. <varname>dept_id</varname>
  373. column in the employee table) are interpreted as supporting the
  374. SDO containment relationships.
  375. Each containment relationship mentioned in the SDO containment relationships
  376. metadata must have a corresponding foreign key present in the
  377. database and defined in the database metadata. The values of the
  378. foreign key columns for containment relationships do not appear in the
  379. data objects, instead each is represented by a containment relationship
  380. from the parent to the child. So the
  381. <varname>co_id</varname>
  382. column in the department row in the database, for example, does not
  383. appear as a property on the department type, but instead as a
  384. containment relationship called
  385. <varname>department</varname>
  386. on the company type.
  387. Note that the foreign key and the parent-child relationship appear to
  388. have opposite senses: the foreign key points from the department to
  389. the company, but the parent-child relationship points from company to
  390. department.
  391. </para>
  392. <para>
  393. The third foreign key in this example, the
  394. <varname>employee_of_the_month</varname>
  395. ,
  396. is handled differently.
  397. This is not mentioned in the SDO containment relationships metadata.
  398. As a consequence this is interpreted in the second way: it becomes
  399. a single-valued non-containment reference on the company object, to
  400. which can be assigned references to SDO data objects of the employee
  401. type. It does appear as a property on the company type. The way to
  402. assign a value to it in the SDO data graph is to have a graph that
  403. contains an employee object through the containment relationships, and
  404. to assign the object to it. This is illustrated in the later examples
  405. below.
  406. </para>
  407. </section>
  408. </section> <!--specifying the metadata sdo.das.rel.metadata.database.model -->
  409. </section> <!-- sdo.das.rel.metadata -->
  410. <section xml:id='sdo.das.rel.examples.one-table'>
  411. <title>One-table examples</title>
  412. <para>
  413. The following set of examples all use the Relational DAS to work with
  414. a data graph containing just one application data object, a single
  415. company and the data just to be found the company table. These examples
  416. do not exercise the power of SDO or the Relational DAS and of course
  417. the same result could be achieved more economically with direct SQL
  418. statements but they are intended to illustrate how to work with the
  419. Relational DAS.
  420. </para>
  421. <para>
  422. For this very simple scenario it would be possible to simplify the
  423. database metadata to include just the company table - if that were done
  424. the second and third arguments to the constructor and the column
  425. specifier used in the query example would become optional.
  426. </para>
  427. <para>
  428. <example>
  429. <title>Creating a data object</title>
  430. <para>
  431. The simplest example is that of creating a single data object and
  432. writing it to the database. In this example a single company object
  433. is created, its name is set to 'Acme', and the Relational DAS is
  434. called to write the changes to the database. The company name is
  435. set here using the property name method. See the
  436. <link linkend="sdo.examples">Examples</link>
  437. section on the SDO extension for other ways of accessing the
  438. properties of an object.
  439. </para>
  440. <para>
  441. Data objects can only be created when you have a data object to
  442. start with, however. It is for that reason that the first call
  443. to the Relational DAS here is to obtain a root object. This is
  444. in effect how to ask for an empty data graph - the special root
  445. object is the true root of the tree. The company data object is
  446. then created with a call to
  447. <function>createDataObject</function>
  448. on the root object. This creates the company data object and inserts
  449. it in the graph by inserting into a multi-valued containment property
  450. on the root object called 'company'.
  451. </para>
  452. <para>
  453. When the Relational DAS is called to apply the changes a simple
  454. insert statement 'INSERT INTO company (name) VALUES ("Acme");'
  455. will be constructed and executed. The auto-generated primary key
  456. will be set into the data object and the change summary will be reset,
  457. so that it would be possible to continue working with the same data
  458. object, modify it, and apply the newer changes a second time.
  459. </para>
  460. <programlisting role="php" xml:id="sdo.das.rel.examples.1c-C">
  461. <![CDATA[
  462. <?php
  463. require_once 'SDO/DAS/Relational.php';
  464. require_once 'company_metadata.inc.php';
  465. /**************************************************************
  466. * Construct the DAS with the metadata
  467. ***************************************************************/
  468. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  469. /**************************************************************
  470. * Obtain a root object and create a company object underneath.
  471. * Make a simple change to the data object.
  472. ***************************************************************/
  473. $root = $das -> createRootDataObject();
  474. $acme = $root -> createDataObject('company');
  475. $acme->name = "Acme";
  476. /**************************************************************
  477. * Get a database connection and write the object to the database
  478. ***************************************************************/
  479. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  480. $das -> applyChanges($dbh, $root);
  481. ?>
  482. ]]>
  483. </programlisting>
  484. </example>
  485. </para>
  486. <para>
  487. <example>
  488. <title>Retrieving a data object</title>
  489. <para>
  490. In this example a single data object is retrieved from the database
  491. - or possibly more than one if there is more than one company
  492. called 'Acme'. For each company returned, the
  493. <varname>name</varname>
  494. and
  495. <varname>id</varname>
  496. properties are echoed.
  497. </para>
  498. <para>
  499. In this example the third argument to
  500. <function>executeQuery</function>,
  501. the column specifier is needed as there are other tables in the
  502. metadata with column names of
  503. <varname>name</varname>
  504. and
  505. <varname>id</varname>.
  506. If there were no possible ambiguity it could be omitted.
  507. </para>
  508. <programlisting role="php" xml:id="sdo.das.rel.examples.1c-R">
  509. <![CDATA[
  510. <?php
  511. require_once 'SDO/DAS/Relational.php';
  512. require_once 'company_metadata.inc.php';
  513. /**************************************************************
  514. * Construct the DAS with the metadata
  515. ***************************************************************/
  516. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  517. /**************************************************************
  518. * Get a database connection
  519. ***************************************************************/
  520. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  521. /**************************************************************
  522. * Issue a query to obtain a company object - possibly more if they exist
  523. ***************************************************************/
  524. $root = $das->executeQuery($dbh,
  525. 'select name, id from company where name="Acme"',
  526. array('company.name', 'company.id') );
  527. /**************************************************************
  528. * Echo name and id
  529. ***************************************************************/
  530. foreach ($root['company'] as $company) {
  531. echo "Company obtained from the database has name = " .
  532. $company['name'] . " and id " . $company['id'] . "\n";
  533. }
  534. ?>
  535. ]]>
  536. </programlisting>
  537. </example>
  538. </para>
  539. <para>
  540. <example>
  541. <title>Updating a data object</title>
  542. <para>
  543. This example combines the previous two, in the sense that in order
  544. to be updated the object must first be retrieved. The application
  545. code reverses the company name (so 'Acme' becomes 'emcA') and then the
  546. changes are written back to the database in the same way that they
  547. were when the object was created. Because the query searches for
  548. the name both ways round the program can be run repeatedly to find
  549. the company and reverse its name each time.
  550. </para>
  551. <para>
  552. In this example the same instance of the Relational DAS is reused
  553. for the
  554. <function>applyChanges</function>,
  555. as is the PDO database handle. This is quite alright; it also
  556. alright to allow the previous instances to be garbage collected
  557. and to obtain new instances. No state data regarding the graph
  558. is held the Relational DAS once it has returned a data graph to
  559. the application. All necessary data is either within the graph itself,
  560. or can be reconstructed from the metadata.
  561. </para>
  562. <programlisting role="php" xml:id="sdo.das.rel.examples.1c-RU">
  563. <![CDATA[
  564. <?php
  565. require_once 'SDO/DAS/Relational.php';
  566. require_once 'company_metadata.inc.php';
  567. /**************************************************************
  568. * Construct the DAS with the metadata
  569. ***************************************************************/
  570. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  571. /**************************************************************
  572. * Get a database connection
  573. ***************************************************************/
  574. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  575. /**************************************************************
  576. * Issue a query to obtain a company object - possibly more if they exist
  577. ***************************************************************/
  578. $root = $das->executeQuery($dbh,
  579. 'select name, id from company where name="Acme" or name="emcA"',
  580. array('company.name', 'company.id') );
  581. /**************************************************************
  582. * Alter the name of just the first company
  583. ***************************************************************/
  584. $company = $root['company'][0];
  585. echo "obtained a company with name of " . $company->name . "\n";
  586. $company->name = strrev($company->name);
  587. /**************************************************************
  588. * Write the change back
  589. ***************************************************************/
  590. $das->applyChanges($dbh,$root);
  591. ?>
  592. ]]>
  593. </programlisting>
  594. </example>
  595. </para>
  596. <para>
  597. <example>
  598. <title>Deleting a data object</title>
  599. <para>
  600. Any companies called 'Acme' or its reverse 'emcA' are retrieved.
  601. They are then all deleted from the graph with unset.
  602. </para>
  603. <para>
  604. In this example they are all deleted in one go by unsetting the
  605. containing property (the property defining the containment
  606. relationship). It is also possible to delete them individually.
  607. </para>
  608. <programlisting role="php" xml:id="sdo.das.rel.examples.1c-RD">
  609. <![CDATA[
  610. <?php
  611. require_once 'SDO/DAS/Relational.php';
  612. require_once 'company_metadata.inc.php';
  613. /**************************************************************
  614. * Construct the DAS with the metadata
  615. ***************************************************************/
  616. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  617. /**************************************************************
  618. * Get a database connection
  619. ***************************************************************/
  620. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  621. /**************************************************************
  622. * Issue a query to obtain a company object - possibly more if they exist
  623. ***************************************************************/
  624. $root = $das->executeQuery($dbh,
  625. 'select name, id from company where name="Acme" or name="emcA"',
  626. array('company.name', 'company.id') );
  627. /**************************************************************
  628. * Delete any companies found from the data graph
  629. ***************************************************************/
  630. unset($root['company']);
  631. /**************************************************************
  632. * Write the change(s) back
  633. ***************************************************************/
  634. $das->applyChanges($dbh,$root);
  635. ?>
  636. ]]>
  637. </programlisting>
  638. </example>
  639. </para>
  640. </section> <!-- one-table -->
  641. <section xml:id='sdo.das.rel.examples.two-table'>
  642. <title>Two-table examples</title>
  643. <para>
  644. The following set of examples all use two tables from the company
  645. database: the company and department tables. These examples exercise
  646. more of the function of the Relational DAS.
  647. </para>
  648. <para>
  649. In this series of examples a company and department are created,
  650. retrieved, updated, and finally deleted. This illustrates the
  651. lifecycle for a data graph containing more than one object. Note that
  652. this example clears out the company and department tables at the start
  653. so that the exact results of the queries can be known.
  654. </para>
  655. <para>
  656. You can find these examples combined into one script called
  657. <filename>1cd-CRUD</filename>
  658. in the
  659. <filename>Scenarios</filename>
  660. directory in the Relational DAS package.
  661. </para>
  662. <para>
  663. <example>
  664. <title>One company, one department - Create</title>
  665. <para>
  666. As in the earlier example of creating just one company data object,
  667. the first action after constructing the Relational DAS is to call
  668. <function>createRootDataObject</function>
  669. to obtain the special root object of the otherwise empty data graph.
  670. The company object is then created as a child of the root object,
  671. and the department object as a child of the company object.
  672. </para>
  673. <para>
  674. When it comes to applying the changes, the Relational DAS has to
  675. perform special processing to maintain the foreign keys that support
  676. the containment relationships, especially if auto-generated primary
  677. keys are involved. In this example, the relationship between the
  678. auto-generated primary key
  679. <varname>id</varname>
  680. in the company table and the
  681. <varname>co_id</varname>
  682. column in the department table must be maintained. When inserting a
  683. company and department for the first time the Relational DAS has to
  684. first insert the company row, then call PDO's
  685. <function>getLastInsertId</function>
  686. method to obtain the auto-generated primary key, then add that as
  687. the value of the
  688. <varname>co_id</varname>
  689. column when inserting the department row.
  690. </para>
  691. <programlisting role="php" xml:id="sdo.das.rel.examples.1cd-C">
  692. <![CDATA[
  693. <?php
  694. require_once 'SDO/DAS/Relational.php';
  695. require_once 'company_metadata.inc.php';
  696. /*************************************************************************************
  697. * Empty out the two tables
  698. *************************************************************************************/
  699. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  700. $pdo_stmt = $dbh->prepare('DELETE FROM COMPANY;');
  701. $rows_affected = $pdo_stmt->execute();
  702. $pdo_stmt = $dbh->prepare('DELETE FROM DEPARTMENT;');
  703. $rows_affected = $pdo_stmt->execute();
  704. /**************************************************************
  705. * Create a company with name Acme and one department, the Shoe department
  706. ***************************************************************/
  707. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  708. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  709. $root = $das -> createRootDataObject();
  710. $acme = $root -> createDataObject('company');
  711. $acme -> name = "Acme";
  712. $shoe = $acme->createDataObject('department');
  713. $shoe->name = 'Shoe';
  714. $das -> applyChanges($dbh, $root);
  715. ?>
  716. ]]>
  717. </programlisting>
  718. </example>
  719. </para>
  720. <para>
  721. <example>
  722. <title>One company, one department - Retrieve and Update</title>
  723. <para>
  724. In this case the SQL query passed to
  725. <function>executeQuery</function>
  726. performs an inner join to join the data from the company
  727. and department tables. Primary keys for both the company and
  728. department tables must be included in the query. The result set
  729. is re-normalised to form a normalised data graph. Note that a
  730. column specifier is passed as the third argument to the
  731. <function>executeQuery</function>
  732. call enabling the Relational DAS to know which column is which in
  733. the result set.
  734. </para>
  735. <para>
  736. Note that the
  737. <varname>co_id</varname>
  738. column although used in the query is not needed in the result set.
  739. In order to understand what the Relational DAS is doing when it builds
  740. the data graph it may be helpful to visualise what the result set
  741. looks like. Although the data in the database is normalised, so that
  742. multiple department rows can point through their foreign key to one
  743. company row, the data in the result set is non-normalised: that is,
  744. if there is one company and multiple departments, the values for the
  745. company are repeated in each row. The Relational DAS has to reverse
  746. this process and turn the result set back into a normalised data graph,
  747. with just one company object.
  748. </para>
  749. <para>
  750. In this example the Relational DAS will examine the result set and
  751. column specifier, find data for both the company and department
  752. tables, find primary keys for both, and interpret each row as
  753. containing data for a department and its parent company. If it has
  754. not seen data for that company before (it uses the primary key to
  755. check) it creates a company object and then a department object
  756. underneath it. If it has seen data for that company before and
  757. has already created the company object it just creates the
  758. department object underneath.
  759. </para>
  760. <para>
  761. In this way the Relational DAS can retrieve and renormalise data
  762. for multiple companies and multiple departments underneath them.
  763. </para>
  764. <programlisting role="php" xml:id="sdo.das.rel.examples.1cd-RU">
  765. <![CDATA[
  766. <?php
  767. require_once 'SDO/DAS/Relational.php';
  768. require_once 'company_metadata.inc.php';
  769. /**************************************************************
  770. * Retrieve the company and Shoe department, then delete Shoe and add IT
  771. ***************************************************************/
  772. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  773. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  774. $root = $das->executeQuery($dbh,
  775. 'select c.id, c.name, d.id, d.name from company c, department d where d.co_id = c.id',
  776. array('company.id','company.name','department.id','department.name'));
  777. $acme = $root['company'][0]; // get the first company - will be 'Acme'
  778. $shoe = $acme['department'][0]; // get the first department underneath - will be 'Shoe'
  779. unset($acme['department'][0]);
  780. $it = $acme->createDataObject('department');
  781. $it->name = 'IT';
  782. $das -> applyChanges($dbh, $root);
  783. ?>
  784. ]]>
  785. </programlisting>
  786. </example>
  787. </para>
  788. <para>
  789. <example>
  790. <title>One company, two departments - Retrieve and Delete</title>
  791. <para>
  792. In this example the company and department are retrieved and
  793. then deleted. It is not necessary to delete them individually
  794. (although that would be possible) - deleting the company object
  795. from the data graph also deletes any departments underneath it.
  796. </para>
  797. <para>
  798. Note the way that the company object is actually deleted using the
  799. PHP unset call. The unset has to be performed on the containing
  800. property which in this case is
  801. the company property on the special
  802. root object. You must use:
  803. <programlisting role="php" xml:id="sdo.das.rel.examples.1cd-CRUD.good-delete">
  804. <![CDATA[
  805. <?php
  806. unset($root['company'][0]);
  807. ?>
  808. ]]>
  809. </programlisting>
  810. and not:
  811. <programlisting role="php" xml:id="sdo.das.rel.examples.1cd-CRUD.bad-delete">
  812. <![CDATA[
  813. <?php
  814. unset($acme); //WRONG
  815. ?>
  816. ]]>
  817. </programlisting>
  818. Simply unsetting
  819. <varname>$acme</varname>
  820. would destroy the variable but leave the data in the data
  821. graph untouched.
  822. </para>
  823. <programlisting role="php" xml:id="sdo.das.rel.examples.1cd-RD">
  824. <![CDATA[
  825. <?php
  826. require_once 'SDO/DAS/Relational.php';
  827. require_once 'company_metadata.inc.php';
  828. /**************************************************************
  829. * Retrieve the company and IT department, then delete the whole company
  830. ***************************************************************/
  831. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  832. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  833. $root = $das->executeQuery($dbh,
  834. 'select c.id, c.name, d.id, d.name from company c, department d where d.co_id = c.id',
  835. array('company.id','company.name','department.id','department.name'));
  836. $acme = $root['company'][0];
  837. $it = $acme['department'][0];
  838. unset($root['company'][0]);
  839. $das -> applyChanges($dbh, $root);
  840. ?>
  841. ]]>
  842. </programlisting>
  843. </example>
  844. </para>
  845. </section>
  846. <section xml:id='sdo.das.rel.examples.three-table'>
  847. <title>Three-table example</title>
  848. <para>
  849. The following examples use all three tables from the company database:
  850. the company, department, and employee tables. These introduce the final
  851. piece of function not exercised by the examples above: the
  852. non-containment reference
  853. <varname>employee_of_the_month</varname>.
  854. </para>
  855. <para>
  856. Like the examples above for company and department, this set of examples
  857. is intended to illustrate the full lifecycle of such a data graph.
  858. </para>
  859. <para>
  860. <example>
  861. <title>One company, one department, one employee - Create</title>
  862. <para>
  863. In this example a company is created containing one department and
  864. just one employee. Note that this example clears out all three tables
  865. at the start so that the exact results of the queries can be known.
  866. </para>
  867. <para>
  868. Note how once the company, department and employee have been created,
  869. the
  870. <varname>employee_of_the_month</varname>
  871. property of the company can be made to point at the new employee.
  872. As this is a non-containment reference, this cannot be done until
  873. the employee object has been created within the graph.
  874. Non-containment references need to be managed carefully.
  875. For example if the employee were now deleted from under the department,
  876. it would not be correct to try to save the graph without
  877. first clearing or re-assigning the
  878. <varname>employee_of_the_month</varname>
  879. property.
  880. The closure rule for SDO data graphs requires that any object pointed
  881. at by a non-containment reference must also be reachable by
  882. containment relationships.
  883. </para>
  884. <para>
  885. When it comes to inserting the graph into the database, the procedure
  886. is similar to the example of inserting the company and department,
  887. but
  888. <varname>employee_of_the_month</varname>
  889. introduces an extra complexity.
  890. The Relational DAS needs to insert the objects working down the tree
  891. formed by containment relationships, so company, then department, then
  892. employee. This is necessary so that it always has the auto-generated
  893. primary key of a parent on hand to include in a child row. But when
  894. the company row is inserted the employee who is employee of the month
  895. has not yet been inserted and the primary key is not known. The
  896. procedure is that after the employee record is inserted and its
  897. primary key known, a final step is performed in which the the
  898. company record is updated with the employee's primary key.
  899. </para>
  900. <programlisting role="php" xml:id="sdo.das.rel.examples.1cde-C">
  901. <![CDATA[
  902. <?php
  903. require_once 'SDO/DAS/Relational.php';
  904. require_once 'company_metadata.inc.php';
  905. /*************************************************************************************
  906. * Empty out the three tables
  907. *************************************************************************************/
  908. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  909. $pdo_stmt = $dbh->prepare('DELETE FROM COMPANY;');
  910. $rows_affected = $pdo_stmt->execute();
  911. $pdo_stmt = $dbh->prepare('DELETE FROM DEPARTMENT;');
  912. $rows_affected = $pdo_stmt->execute();
  913. $pdo_stmt = $dbh->prepare('DELETE FROM EMPLOYEE;');
  914. $rows_affected = $pdo_stmt->execute();
  915. /*************************************************************************************
  916. * Create a tiny but complete company.
  917. * The company name is Acme.
  918. * There is one department, Shoe.
  919. * There is one employee, Sue.
  920. * The employee of the month is Sue.
  921. *************************************************************************************/
  922. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  923. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  924. $root = $das -> createRootDataObject();
  925. $acme = $root -> createDataObject('company');
  926. $acme -> name = "Acme";
  927. $shoe = $acme -> createDataObject('department');
  928. $shoe -> name = 'Shoe';
  929. $shoe -> location = 'A-block';
  930. $sue = $shoe -> createDataObject('employee');
  931. $sue -> name = 'Sue';
  932. $acme -> employee_of_the_month = $sue;
  933. $das -> applyChanges($dbh, $root);
  934. echo "Wrote back Acme with one department and one employee\n";
  935. ?>
  936. ]]>
  937. </programlisting>
  938. </example>
  939. </para>
  940. <para>
  941. <example>
  942. <title>One company, one department, one employee - Retrieve and update</title>
  943. <para>
  944. The SQL statement passed to the Relational DAS is this time an inner
  945. join that retrieves data from all three tables. Otherwise this example
  946. introduces nothing that has not appeared in a previous example.
  947. </para>
  948. <para>
  949. The graph is updated by the addition of a new department and employee
  950. and some alterations to the name properties of the existing objects
  951. in the graph. The combined changes are then written back. The
  952. Relational DAS will process and apply an arbitrary mixture of
  953. additions, modifications and deletions to and from the data graph.
  954. </para>
  955. <programlisting role="php" xml:id="sdo.das.rel.examples.1cde-RU">
  956. <![CDATA[
  957. <?php
  958. require_once 'SDO/DAS/Relational.php';
  959. require_once 'company_metadata.inc.php';
  960. /*************************************************************************************
  961. * Find the company again and change various aspects.
  962. * Change the name of the company, department and employee.
  963. * Add a second department and a new employee.
  964. * Change the employee of the month.
  965. *************************************************************************************/
  966. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  967. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  968. $root = $das->executeQuery($dbh,
  969. "select c.id, c.name, c.employee_of_the_month, d.id, d.name, e.id, e.name " .
  970. "from company c, department d, employee e " .
  971. "where e.dept_id = d.id and d.co_id = c.id and c.name='Acme'",
  972. array('company.id','company.name','company.employee_of_the_month',
  973. 'department.id','department.name','employee.id','employee.name'));
  974. $acme = $root['company'][0];
  975. $shoe = $acme->department[0];
  976. $sue = $shoe -> employee[0];
  977. $it = $acme->createDataObject('department');
  978. $it->name = 'IT';
  979. $it->location = 'G-block';
  980. $billy = $it->createDataObject('employee');
  981. $billy->name = 'Billy';
  982. $acme->name = 'MegaCorp';
  983. $shoe->name = 'Footwear';
  984. $sue->name = 'Susan';
  985. $acme->employee_of_the_month = $billy;
  986. $das -> applyChanges($dbh, $root);
  987. echo "Wrote back company with extra department and employee and all the names changed (Megacorp/Footwear/Susan)\n";
  988. ?>
  989. ]]>
  990. </programlisting>
  991. </example>
  992. </para>
  993. <para>
  994. <example>
  995. <title>One company, two departments, two employees - Retrieve and delete</title>
  996. <para>
  997. The company is retrieved as a complete data graph containing five
  998. data objects - the company, two departments and two employees.
  999. They are all deleted by deleting the company object. Deleting an
  1000. object from the graph deletes all the object beneath it in the graph.
  1001. Five SQL DELETE statements will be generated and executed. As always
  1002. they will be qualified with a WHERE clause that contains all of the
  1003. fields that were retrieved, so that any updates to the data in the
  1004. database in the meantime by another process will be detected.
  1005. </para>
  1006. <programlisting role="php" xml:id="sdo.das.rel.examples.1cde-RD">
  1007. <![CDATA[
  1008. <?php
  1009. require_once 'SDO/DAS/Relational.php';
  1010. require_once 'company_metadata.inc.php';
  1011. /*************************************************************************************
  1012. * Now read it one more time and delete it.
  1013. * You can delete part, apply the changes, then carry on working with the same graph but
  1014. * care is needed to keep closure - you cannot delete the employee who is eotm without
  1015. * reassigning. For safety here we delete the company all in one go.
  1016. *************************************************************************************/
  1017. $das = new SDO_DAS_Relational ($database_metadata,'company',$SDO_containment_metadata);
  1018. $dbh = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
  1019. $root = $das->executeQuery($dbh,
  1020. "select c.id, c.name, c.employee_of_the_month, d.id, d.name, e.id, e.name " .
  1021. "from company c, department d, employee e " .
  1022. "where e.dept_id = d.id and d.co_id = c.id and c.name='MegaCorp';",
  1023. array('company.id','company.name','company.employee_of_the_month',
  1024. 'department.id','department.name','employee.id','employee.name'));
  1025. $megacorp = $root['company'][0];
  1026. unset($root['company']);
  1027. $das -> applyChanges($dbh, $root);
  1028. echo "Deleted the company, departments and employees all in one go.\n";
  1029. ?>
  1030. ]]>
  1031. </programlisting>
  1032. </example>
  1033. </para>
  1034. </section>
  1035. </chapter>
  1036. <!-- Keep this comment at the end of the file
  1037. Local variables:
  1038. mode: sgml
  1039. sgml-omittag:t
  1040. sgml-shorttag:t
  1041. sgml-minimize-attributes:nil
  1042. sgml-always-quote-attributes:t
  1043. sgml-indent-step:1
  1044. sgml-indent-data:t
  1045. indent-tabs-mode:nil
  1046. sgml-parent-document:nil
  1047. sgml-default-dtd-file:"../../../manual.ced"
  1048. sgml-exposed-tags:nil
  1049. sgml-local-catalogs:nil
  1050. sgml-local-ecat-files:nil
  1051. End:
  1052. vim600: syn=xml fen fdm=syntax fdl=2 si
  1053. vim: et tw=78 syn=sgml
  1054. vi: ts=1 sw=1
  1055. -->