Yes Magento does this out of the box.
Against each product assign 'cross sell' products, using the cross sell tab on the left. When you first get to it, if the list is empty press the 'reset filter' button to show all the products in the store. Find the ones you want and put a tick next to them, then click save.
The block you are after is;
<block type="checkout/cart_crosssell" name="checkout.cart.crosssell" as="crosssell" template="checkout/cart/crosssell.phtml"/>
Which most template load within "content" in the layout/checkout.xml layout file of your theme,
<checkout_cart_index translate="label">
////
<reference name="content">
///// HERE
</reference>
/////
</checkout_cart_index>
And then add this to the checkout/cart.phtml template (if it's not there already);
<?php echo $this->getChildHtml('crosssell') ?>
But you can add it to wherever you wish.
To handle these products not appearing elsewhere, set their visibility to 'catalog' and either put them in a category that isnt visible or dont add them to a category.
TO ANSWER YOUR SECOND QUESTION...
You asked how you could remove all 'add on' items from the basket if there are no 'main product' items in the basket. Here is a quick solution that will do this.
Create a custom select product attribute, give it ID code 'product_type_var' and give it 2 options 'Main Product' and 'Addon Product'. Add it to your product attribute set and set the values against the appropriate products.
You can then run the following code against the basket. Ideally you would create a module with an event observer - but for the sake of this example, you could also place this code at the top of
app/design/frontend/XXX/YYY/template/checkout/cart.phtml
Here's the code;
$quote = Mage::getSingleton('checkout/session')->getQuote();
$needsAction = true;
$toRemove = array();
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$productLoad = Mage::getModel('catalog/product')->load($product->getId());
$customVariable = $productLoad->getResource()->getAttribute('product_type_var')->getFrontend()->getValue($productLoad);
if($customVariable == 'Main Product') {
$needsAction = false;
break; // No need to do anything
}
if($customVariable == 'Addon Product') {
$toRemove[] = $productLoad->getId(); // Build list of addon IDs
}
}
if($needsAction && (!empty($toRemove))) {
// There are no Main Products and 1 or more Addons
foreach($toRemove as $removeId) {
$quote->removeItem($removeId)->save();
}
}
Revision 3
To make sure any 'addon' products only remain in the cart if they relate to a specific 'main product' found in the cart, try this;
$quote = Mage::getSingleton('checkout/session')->getQuote();
$allowedUpsells = array();
$upsellsInCart = array();
$allIdsInCart = array();
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$productLoad = Mage::getModel('catalog/product')->load($product->getId());
$customVariable = $productLoad->getResource()->getAttribute('product_type_var')->getFrontend()->getValue($productLoad);
if($customVariable == 'Main Product') {
$allIdsInCart[] = $productLoad->getId(); // Build list of all products in the cart
$upsells = $productLoad->getUpSellProductCollection(); // Get this products available upsells
foreach($upsells as $upsell) {
$allowedUpsells[] = $upsell->getId(); // Build full list of allowed addon IDs
}
}
if($customVariable == 'Addon Product') {
$allIdsInCart[] = $productLoad->getId(); // Build list of all products in the cart
$upsellsInCart[] = $productLoad->getId(); //Build full list of addon IDs
}
}
if(!empty($upsellsInCart)) { // Upsells might need attention
$allowedVsInCart = array_intersect($allowedUpsells, $allIdsInCart); // Remove other upsells that are avaiable to the product but not in the cart
$toBeRemoved = array_diff_assoc($allowedVsInCart, $upsellsInCart); // Now find the products in the cart that shouldnt be
if(!empty($toBeRemoved)) {
foreach($toBeRemoved as $removeId) {
$quote->removeItem($removeId)->save();
}
}
}