Как класть товар в корзине с разными опциями?
Это скорее вопрос не столько реализации корзины, а организации собственно справочника номенклатур. Ничего нового тут нет. Что ботинки разных размеров продавать, что пиццы. Можно считать разные размеры разной номенклатурой, а можно ввести какие-нибудь "типы" или "категории" номенклатур. Дело вкуса, делают и так и так.
А вот добавки я бы считал просто отдельными номенклатурами. Так как "Ананас" за 59 рублей он что к одной пицце, что к другой - всё тот же ананас! Для удобства я бы их выделил в отдельную группу, позиции из которой не предлагаются в основном прайсе (где сами пиццы), а только после выбора основной позиции. 
В корзину добавляется товар из основной группы (пицца), затем предлагается добавить товары из другой группы (добавки - грибы, ананасы и т.д.)
В чем именно проблема реализации?
Добавки - это отдельные товары из соответствующей категории Добавки, а размеры пиццы - это лишь модификации этого товара.
В корзине идентификатором товаров пусть выступает не id товара, а ключ, который генерируется в бэке перед добавлением. Например ключ каждого товара будет равен шифрованию md5 строки
$productKey = md5(id_товара. цена_товара. модификация_товара(размер). json_encode(добавки));
И пишите массив товаров корзины например в сессию, где ключом будет выступать сгенерированный key.
Тогда, условно, при добавлении Маграгриты 25см с халапеньо и Маргариты 30см без добавок, в корзине будут отображаться два разных товара, так как у них разный key.
И в сессии это может выглядеть например так:
[идентификатор_сессии][id-заказа][cart] = array (
  '984b707b193e29fb93c9d6e05a585ce6' => array("Маргарита", 100 рублей, 25см, 1шт, {"халапеньо"}),
  '977b713b193e29fb93c9d6e05a513ke2' => array("Маргарита", 100 рублей, 30см, 1шт, {})
);