<<

. 72
( 132 .)



>>

$error = $this->error;
if ($error)
user_error($error, E_USER_ERROR);
}

PRODUCTS
Before we get started explaining this class, we want to restate that it is very similar
to the other classes in this application. If you understand how this class works, the
rest of the classes should be relatively easy to figure out.

__construct() This is the constructor of the class. It is very brief.

function __construct()
{
parent::__construct();
$args = func_get_args();
call_user_func_array(array($this, ˜build™), $args);
$this->id =& $this->product_id;
}
Chapter 12: Catalog 423

Note that this constructor runs if a Product object is instantiated within a script.
But after the object is instantiated, the information associated with the product_id
is not automatically loaded. The fetch_from_db() method, which you saw in the
Base class, is needed for that.

price() You only want to store prices greater than zero. This method takes care of
that. Remember that in the Base class™s build() method, if your object has a
method defined with the same name as an incoming property value, that new value
is passed to the method rather than just being assigned to the property. So if you
have a form with a field named price, it is submitted to your script with the POST
method; when you call $product->build($_POST), you end up calling $product
->price($_POST[˜price™]).

function price($newprice=NULL)
{
if ($newprice !== NULL)
$this->price = $newprice;
if ($this->price == 0)
$this->price = NULL;
return $this->price;
}

list_all_children() and list_children() These methods override methods of the
CatalogBase class, the parent class of Product and itself a child class of the main
Base class we looked at earlier. Here, we are overriding the parent class™ methods to
call an additional method of our own, add_notes() (described in the next section).

function list_all_children()
{
parent::list_all_children();
if (count($this->child_ids) > 0)
{
$this->add_notes(
$this->all_children
, array_keys($this->child_ids)
);
}
return $this->all_children;
}

function list_children()
{
parent::list_children();
424 Part IV: Not So Simple Applications

if (count($this->child_ids) > 0)
{
$this->add_notes(
$this->children
, array_keys($this->child_ids)
);
}
return $this->children;
}

add_notes() This method checks the database to see if any of the children of this
product are mapped to a status other than “Available.” If so, it adds a notes element
to the array entry for those children describing that status (for example, “Not avail-
able in orange”).

function add_notes(&$kids,$ids)
{
if (empty($kids) or empty($ids))
return;

$query = ˜select m.product_id, t.status, s.style
from product_style_map m, styles s, status t
where m.product_id in (!)
and m.style_id = s.style_id
and m.status_id in (?,?)
and m.status_id = t.status_id™
;
$stmt = $this->dbh()->prepare($query);
$result = $this->dbh()->execute(
$stmt
, array(
implode(˜,™, $ids)
, Product::NotAvailable
, Product::OutOfStock
)
);
$notes = array();
while ($r = $result->fetchRow())
$notes[$r[˜product_id™]][$r[˜status™]][] = $r[˜style™];
$result->free();

foreach ($kids as $i => $c)
{
$cnotes = array();
Chapter 12: Catalog 425

if (isset($notes[$c[˜product_id™]]))
{
foreach ($notes[$c[˜product_id™]] as
$status=>$styles)
{
$cnotes[] = $status.™ in ˜.implode(˜,™,$styles);
}
}
$c[˜notes™] = implode(˜; ˜, $cnotes);
$kids[$i] = $c;
}
return;
}

list_unmapped_styles() This method retrieves all root-level styles that are not
mapped to the current product (or one of its parents). It is used in the administra-
tion page for the Product class to display a list of styles available to be mapped to
the product.

function list_unmapped_styles()
{
$query = ˜select distinct s.style_id, s.parent_id
, s.style, s.description, m.product_id as map_id
from styles s
left join product_style_map m
on s.style_id = m.style_id and m.product_id in (!)
where ifnull(s.parent_id,0) = 0
having map_id is null™
;
$stmt = $this->dbh()->prepare($query);
$ids = $this->get_parent_ids();
$ids[] = $this->product_id;
$result=$this->dbh()-
>execute($stmt,array(implode(˜,™,$ids)));
$styles = array();
while ($row = $result->fetchRow())
{
unset($row[˜map_id™]);
$styles[] = $row;
}
$result->free();
return $styles;
}
426 Part IV: Not So Simple Applications

list_mapped_styles() This method retrieves all the product-style mappings for
the current product and its children, whether those mappings are explicitly stored
in the database or are inherited. You use it by running a query against the style
table, with a left join to the product_style_map table to get any mappings to the
current product or one of its parents. If a style is not explicitly mapped to any of
those products, the product ID field from the query (aliased to the column name
map_product_id) will be null. When you come upon such a row as you walk
through the results of the query, you check to see if there was an explicit mapping to
the parent of the style. If there was, you copy the status from that mapping. Because
you can™t create a child record without having created its parent first, parent style ID
values are always smaller than those of child IDs. By sorting by parent ID and then
child ID, you know that you will run into the parents first ” and so the records will
be there for the child style to find.

function list_mapped_styles()
{
$this->styles = array();
$this->mapped_styles = array();
$ids = $this->get_parent_ids();
$ids[] = $this->product_id;
$idlist = implode(˜,™, $ids);
$bind = array($idlist);

// we could omit this part and the rest of this
// function would still run correctly. but if you
// had a large number of styles, only a few of which
// applied to any one product line, you™d end up
// throwing away more rows than you used. so let™s
// narrow the search down a bit by limiting it to
// only descendants of root styles mapped to this
// product or one of its parents

$stylewhere = ˜™;
if ($this->product_id)
{
$style_ids = $this->dbh()->getCol(
˜select m.style_id from product_style_map m, styles
s
where m.product_id in (!) and m.style_id =
s.style_id
and ifnull(s.parent_id,0) = 0 ˜
,0
, array($idlist)
);
$style_ids = $this->get_child_ids(
Chapter 12: Catalog 427

˜style_id™
, ˜styles™
, $style_ids
);
if (count($style_ids) > 0)
{
$bind[] = implode(˜,™, $style_ids);
$stylewhere = ˜where s.style_id in (!)™;
}
}

$query = <<<EOQ
select distinct s.style_id as id, s.style_id
, ifnull(s.parent_id,0) as parent_id
, s.style, s.description
, m.product_id as map_product_id
, m.status_id, m.price, m.price_type_id
from styles s
left join product_style_map m
on s.style_id = m.style_id
and m.product_id in (!)
$stylewhere
order by parent_id, style_id, map_product_id
EOQ;
$result = $this->dbh()->query($query, $bind);
$values = array();

// what we want to end up with:
// values = array(
// parent_id => array(style_id=>row,style_id=>row)
// , parent_id => array(style_id=>row,style_id=>row)
// )
while ($row = $result->fetchRow())
{
if (empty($row[˜map_product_id™]))
{
// throw out unmapped root styles
if ($row[˜parent_id™] == 0)
continue;

// throw out children of unmapped parents
if (!isset($this->styles[$row[˜parent_id™]]))

// inherit values from parent
$prow = &$this->styles[$row[˜parent_id™]];
428 Part IV: Not So Simple Applications

$row[˜map_product_id™] = $prow[˜map_product_id™];
$row[˜price™] = $prow[˜price™];
$row[˜price_type_id™] = $prow[˜price_type_id™];
if ($prow[˜status_id™] == Product::NotAvailable
|| $prow[˜status_id™] == Product::OutOfStock
|| $prow[˜status_id™] == Product::ParentUnavailable
)
{
$row[˜status_id™] = Product::ParentUnavailable;
}
else
{
$row[˜status_id™] = Product::Inherited;
}
}
$values[$row[˜parent_id™]][$row[˜id™]] = $row;
$this->styles[$row[˜style_id™]] = $row;
}
$result->free();

<<

. 72
( 132 .)



>>