/** * @see Souk::auction * take the item out of the seller's account and place it in escrow when creating the auction. * set up the quantity, which can be an integer or some other quantity from stockpile. */ function auction($l, array $data = NULL) { // wrap in a try/catch block so we can handle the transaction correctly, and rollback // if need be. try { // kick off a transaction. if there isn't one attached, create one. Transaction::start(); // convert the data coming in, probably an array,into a Listing. $listing = Util::listing($l); // if no seller passed in, use the current user. if (!isset($listing->seller)) { $listing->seller = $this->user(); } // if no quantity specified, default to 1. if (!isset($listing->quantity)) { $listing->quantity = 1; } // connect to the seller's item account in stockpile $seller = $this->itemAccount($listing->seller); // also need their escrow account $escrow = $this->itemEscrow($listing->seller); // bind them together in a stockpile transfer object so we can move items from // their inventory into escrow. Prevents the seller from trading away the items while // simultaneously auctioning them off. $transfer = $this->transfer($seller, $escrow); // if the inventory type is serial, then we need a stockpile quantity object. // if an integer or some other value was passed in for quantity, convert it into // the appropriate stockpile_quantity so we are sure we are vending the proper // serials. if ($seller->coreType() == 'serial' && !$listing->quantity instanceof \Gaia\Stockpile\Quantity) { $listing->quantity = $seller->get($listing->item_id)->grab($listing->quantity); } // subtract the quantity from the inventory ... adds it transparently to escrow. $transfer->subtract($listing->item_id, $listing->quantity, $this->prepData($data, $listing, 'auction')); // now, we need to prep the quantity so that it can be stored in the listing in // the database. we assign an export of the object to the stockpile_quantity attribute of the listing // that way when we deserialize it back out, we can reconstruct the object. if ($listing->quantity instanceof \Gaia\Stockpile\Quantity) { $listing->stockpile_quantity = $listing->quantity->export(); } // turn the quantity into a scalar representation so souk can write the quantity into the db row. $listing->quantity = $seller->quantify($listing->quantity); // make sure the listing we return is deserialized correctly $listing = $this->prepListing($this->core->auction($listing)); // commit the transaction, if we created it internally. Transaction::commit(); // all done. return $listing; } catch (Exception $e) { // ewww, something nasty happened. revert the transaction. Transaction::rollback(); // toss the exception again up the chain. throw $e; } }
/** * Get multiple listings back, by id * @return array */ public function fetch(array $ids, $lock = FALSE) { // stub out the result set so when we populate the data later, we will be // returning the rows in the correct order as requested. // important when we do things like pass the list of ids from search into here, // where the ordering of ids matters. $result = array_fill_keys($ids, NULL); // we have to query by shard, which means we need to parse all the ids passed in // and group them by shard. $shardlist = array(); foreach ($ids as $id) { list($shard, $row_id) = Souk\Util::parseId($id); if (!isset($shardlist[$shard])) { $shardlist[$shard] = array(); } $shardlist[$shard][] = $row_id; } if ($lock && !Transaction::atStart()) { Transaction::add($this->db); } // loop through the ids grouped by shard foreach ($shardlist as $shard => $row_ids) { // query always by shard and row_id. this query is a primary key lookup // so it should always be very efficient. $table = $this->table($shard); if (\Gaia\Souk\Storage::isAutoSchemaEnabled()) { $this->create($table); } $sql = "SELECT row_id, bid, proxybid, bidcount, item_id, quantity, price, step, created, expires, buyer, seller, bidder, reserve, closed, touch FROM {$table} WHERE row_id IN (?)"; if ($lock) { $sql .= ' FOR UPDATE'; } $rs = $this->execute($sql, $row_ids); // grab the rows returned and populate the result as Souk\listings. $row_ids = array(); while ($row = $rs->fetch()) { $row_id = $row_ids[] = $row['row_id']; unset($row['row_id']); $listing = Souk\Util::listing($row); $listing->id = Souk\Util::composeId($shard, $row_id); $result[$listing->id] = $listing; } $rs->free(); // did we get any rows back? if not, move on to the next shard. if (count($row_ids) < 1) { continue; } // populate the listing with the serialized attributes that didn't map to any of // the predefined columns in souk. in most cases this table will be empty, but // we query it anyway to be sure. // don't need a row lock on this table because we have one on the main listing table. // since we always access these two tables in tandem, the prior row lock should // serialize the requests and work for both. $table_attr = $table . '_attr'; $sql = "SELECT row_id, attributes FROM {$table_attr} WHERE row_id IN ( %i )"; $rs = $this->execute($sql, $row_ids); // no rows? no problem, just skip it. that is expected. if ($rs->affected() < 1) { continue; } // someone stored attributes! merge them in. // stored as json in the db. deserialize and layer on top of the listing object. while ($row = $rs->fetch()) { $id = Souk\Util::composeId($shard, $row['row_id']); if (!isset($result[$id])) { continue; } $attributes = json_decode($row['attributes'], TRUE); if (!is_array($attributes)) { continue; } $listing = $result[$id]; foreach ($attributes as $k => $v) { if ($k == 'id') { continue; } $listing->{$k} = $v; } } // free the query result. $rs->free(); } // remember how we populated with nulls at the beginning? // remove any that are still null, now that we are done. foreach (array_keys($result, NULL, TRUE) as $k) { unset($result[$k]); } // return the result. return $result; }