Hey Tom,

any time.

The reason to keep orders and the cart separate is an issue of
separation of concerns, where we're trying to avoid having classes do
too many things (for all the obvious reasons of maintainability,
easier unit testing and future flexibility).

In our system orders are considered part of the persistence layer
(simple propel objects really), so they fundamentally work like data
storage and need to be viewable in different applications (admin area,
customer service, automated export). This makes the order more of a
historic document than an active element, and we feel this describes
better what it really is.

The cart on the other hand is an object that assembles an order, (an
actor), and for that it needs to interact with lots of other objects,
such as the current user (logged in, or guest?), shipping and tax
services, promotions, etc. As you can see, that becomes quite a lot of
code to drag around if you were to make this part of the order, and
none of that is needed any more after an order is placed and post-
checkout life-cycle begins. Also, this would potentially tie orders to
one specific application (at least our cart is aware of the user,
although I guess you could handle that differently).

So, like Antoine said, it's easier to have createOrder() and
loadOrder($order) methods on the cart, and otherwise keep them
separate.
Does that make sense?

As for the promotion classes, in our system there are model classes
which represent a "PromotionEvent", and store date and configuration
of a promotion. At runtime, this model class is used to create and
configure a more lightweight promotion class which can only apply
itself to the cart. Same for criteria by the way, one class for
storage, one for runtime. So, for each promotion at runtime you have a
minimum of 4 classes going to work, plus the cart. Plus whatever else
they need to do their job.

The reason for this separation is the same as above. Basically, I look
at propel classes as storage, and I like to code my systems to rely as
little as possible on one particular storage medium. For example, if I
were to switch to Doctrine (no intentions), I would only have to swap
out the "dumb" classes without touching the ones containing the logic.
Oh, and there's another reason.. for performance reason we cache
promotions between certain checkout steps, and it's easy to serialize
simple objects, while serializing propel objects is both tricky and
introduces potential for stale data.

That help?
Daniel






On Mar 30, 6:09 am, Tom Haskins-Vaughan <t...@templestreetmedia.com>
wrote:
> Thanks, guys! That's a great help. I will most probably take you up on
> your offer of help! :)  In the meantime, I have a couple of follow on
> questions:
>
> 1. What do you see the benefits of having a separate cart and order objects?
>
> 2. The Promotion classes were the logic takes place, are they also
> model classes or are they separate classes?
>
> Thanks again.
>
> Tom
>
> On Tue, Mar 30, 2010 at 2:00 AM, Richtermeister <nex...@gmail.com> wrote:
> > Hey Tom,
>
> > I wrote the ecommerce part ofwww.skinmedica.com, which uses a quite
> > complex promotions setup and took some iterations to get it right. I'd
> > be happy to help.
>
> > Our cart works similar to what Antoine said, where the shopping cart
> > is distinct from the final order, and mostly responsible for keeping
> > track of what's in it for calculating prices (shipping, tax, total,
> > etc. - the cart is actually composed of different independent service
> > classes to look up shipping, & tax via webservices). Generally we try
> > to keep the cart very limited in what it knows about the rest of the
> > system.
>
> > The way the promotions work is that they're being applied to the cart
> > without the cart knowing much about what is happening. A promotion is
> > basically handed the entire cart and it gets to decide whether it
> > applies. It does that by using a separate class, a promotion
> > criterion, which depending on its type can apply different logic to
> > trigger whether it applies. For example, we have promotion criteria
> > that trigger based on:
> > - a certain cart subtotal
> > - whether a certain product is in the cart
> > - whether the customer is new
> > - etc.
> > Those are all different classes, extending a BasePromotionCriterion,
> > that are further customizable via parameters from an admin area.
>
> > When the promotion applies, it gets to make changes to the cart. Those
> > changes, in turn, depend on the type of the promotion class.
> > We have promotions that:
> > - apply a cart discount
> > - apply a product discount
> > - add a free product
> > - make shipping free of charge
> > - etc.
>
> > By combining criterion classes with promotions classes, and passing a
> > few parameters along, you can create virtually any combination of
> > promotions, and when you find limitations, you can simply add more
> > classes. Plus, this is fairly easy to administer.
>
> > The hardest part is issues like precedence and mutual exclusivity. For
> > example, if a promotion applies a cart discount at a certain subtotal,
> > then that in turn can lower the subtotal. So, the system needs to be
> > smart enough not to remove the promotion again. Or, there are
> > szenarios where 2 free shipping promotions are applicable, but the
> > system should only apply one (usually the cheaper one, but that can
> > vary).
> > So, basically we apply weights to classes to figure out in which order
> > the promotions apply, and there is an override parameter that enables
> > each promotion to override others, for some manual finetuning.
> > Sorting that all out is a matter of a series of interesting loops with
> > callbacks that cost me a few sleepless nights :)
>
> > Does that help? Let me know if you have any specific questions and I'd
> > be happy to help more.
> > Daniel
>
> > On Mar 29, 5:56 pm, Tom Haskins-Vaughan <t...@templestreetmedia.com>
> > wrote:
> >> (sf1.4/Doctrine)
>
> >> Hi all,
>
> >> Let's say I'm developing an ecommerce solution. I have the following 
> >> models:
>
> >> Order - takes a shopping cart all the way through to an actual order.
>
> >> OrderItem - link between the Order and one or more Products
>
> >> Product - a catalogue of products
>
> >> Promotion - e.g., buy one get one free, or 10% off item, buy these
> >> both for $10, etc
>
> >> The part I'm interested in is the promotions. I *could* do the logic
> >> in the Order model like so:
>
> >> $order = new Order();
> >> ...
> >> $order->applyPromotion($promotion);
>
> >> But there is likely to be quite a lot of logic, depending on what kind
> >> of promotion it is (adding OrderItems, discounting Orders, etc), so my
> >> instinct was to create a PromotionManager class so for example:
>
> >> $promoManager = new PromotionManager();
> >> $promoManager->appyPromotion($promotion, $order);
>
> >> Is this a better way to go, or am I causing myself needless abstraction?
>
> >> I know there's more than one way to skin a cat, but I'm trying hard to
> >> get to grips with the best way.
>
> >> Anyway, thanks in advance.
>
> >> Tom
>
> > --
> > If you want to report a vulnerability issue on symfony, please send it to 
> > security at symfony-project.com
>
> > You received this message because you are subscribed to the Google
> > Groups "symfony users" group.
> > To post to this group, send email to symfony-users@googlegroups.com
> > To unsubscribe from this group, send email to
> > symfony-users+unsubscr...@googlegroups.com
> > For more options, visit this group at
> >http://groups.google.com/group/symfony-users?hl=en
>
> > To unsubscribe from this group, send email to 
> > symfony-users+unsubscribegooglegroups.com or reply to this email with the 
> > words "REMOVE ME" as the subject.

-- 
If you want to report a vulnerability issue on symfony, please send it to 
security at symfony-project.com

You received this message because you are subscribed to the Google
Groups "symfony users" group.
To post to this group, send email to symfony-users@googlegroups.com
To unsubscribe from this group, send email to
symfony-users+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/symfony-users?hl=en

Reply via email to