Post by MarkF » Thu Mar 26, 2009 8:27 am

Hi,

can we have the ability to link or associate products please?

Example - product is a necklace, but it's linked to two other products - a matching bracelet & ear-rings and vice-versa.
So when the customer views the product page for one of the three products, they can also see links & images for the other two - great up-sell facility!

Thanks
Mark :)

New member

Posts

Joined
Tue Mar 17, 2009 9:54 pm

Post by Qphoria » Thu Mar 26, 2009 9:15 am

You need someone to port the "related products" contrib from 0.x

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by MarkF » Thu Mar 26, 2009 6:44 pm

Thanks Q
You need someone to port the "related products" contrib from 0.x
Any volunteers? ;)
This would really help with my current project, but is beyond my (slowly growing) skills!

Just curious, but any idea as to why some of the great contribs produced haven't been absorbed into the core product to enhance functionality out of the box and reduce 'hacking time' for us all?

Regards
Mark :)

New member

Posts

Joined
Tue Mar 17, 2009 9:54 pm

Post by Qphoria » Thu Mar 26, 2009 6:57 pm

Unfortunately, the 0.x team and 1.x teams were very segregated. None of the fixes or merged mods from 0.x are in 1.x. But also because the 0.x mods weren't outright compatible with 1.x. So first you'd need to port the mod to 1.x, and then perhaps daniel will merge them into the core. But there again lies a problem. People can be very meticulous with their code, so a user contributed mod might work, but its not always coded to the same standards.... which means that the code must be ported additionally

Then it comes down to just what should be in the core vs what should be a module

For instance, Category Descriptions should be in the core, as it affects all people, all languages, universally. We added it to 0.7.9 final

But certain localized payment modules like sedex (a brazillian payment) shouldn't really be in the core as most of the world has no need for it and its just extra bloat. So that would be best as a contrib

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by MarkF » Sun Mar 29, 2009 11:20 pm

Thanks Q.

Linked products sounds like core functionality to me...?

I might have a go at porting this across, but I've no idea where to start - any pointers?

Thanks,
Mark :)

New member

Posts

Joined
Tue Mar 17, 2009 9:54 pm

Post by Qphoria » Mon Mar 30, 2009 12:43 am

I agree, it should be in the core. I'd start with looking at the original related products contrib on the OpenCart Zero forums:
http://forum.opencart0.com/index.php/topic,2160.0.html

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by MarkF » Mon Mar 30, 2009 1:18 am

Thanks - I'll take a look

Regards
Mark :)

New member

Posts

Joined
Tue Mar 17, 2009 9:54 pm

Post by Daniel » Mon Mar 30, 2009 4:43 am

I'm going to start a feature request thread.

User avatar
Administrator

Posts

Joined
Fri Nov 03, 2006 6:57 pm

Post by MarkF » Mon Mar 30, 2009 6:33 am

Thanks Daniel - I've added it and a few others to that thread!

Regards
Mark :)

New member

Posts

Joined
Tue Mar 17, 2009 9:54 pm

Post by sturman » Mon Mar 30, 2009 11:13 am

I created the requested Linked Products contribution:

http://www.opencart.com/index.php?route ... ion_id=112

The above link is simply a zipped version of a text file with a list of instructions of what code to add to which files to make it all work. I will copy that same text here, which lists all the installation steps and explains why I chose this method of installation:

Related Products Feature, version 1.x


With this contribution you can define one or more related products for each product. For example, suppose you are selling a blue men's shirt, then you can define a red tie and gold cuff links as related products. These two products will then automatically be listed with the shirt as recommended mathing products to buy. The links work both ways, so in this example the blue shirt will also automatically be shown as recommended matching product with the red tie and the gold cuff links.

In the Admin, when you edit or create a new product a new tab "Related" is shown. There you will see a list of all products except the current product. Any one or more of those products can be checked to indicate it as a related product. A problem with this is that it might not work well with a store with a large number of products (say a few hundred or more), because this will make the product list very long and therefore it may be difficult to scroll to the products you want to check.

This contribution is based in part on the Related Products module http://forum.opencart0.com/index.php/topic,2160.0.html by Mike for version 0.7.9-RC3, which in turn was an update of the contribution http://forum.opencart0.com/index.php/topic,1244.0.html by Jarek.

Most contributions/modules are presented as a collection of files to overwrite. The problem with that is that this makes those contributions become outdated very fast. For example, I am using version 1.1.8 while at this time the latest downloadable version of OpenCart is 1.2.5. So if someone with 1.2.5 installed overwrote certain files containing my contribution based on 1.1.8 files, that would create a problem because parts of the files might introduce old bugs which had been fixed or they might be incompatible with 1.2.5. Or suppose you have made your own changes in some files. Then you would have to redo all those changes in the files being overwritten.

For these reasons, I am presenting my contribution not in the form of complete files to be overwritten, but in the form of an instruction list of what to change in which files. That way newer file versions, and your own hacks, will not be overwritten. You will only add new things without modifying all the rest, so that there is much less risk of introducing old bugs or incompatibilities. Also, if at a later time you need to port this contribution to a future big version update, say 2.x, it will be much easier to do based on a list of instructions instead of a collection of files. Of course I hope this contribution will be added to the core, to prevent this porting problem all together :-)

This method also makes it easier for you to see what exactly is being changed or added, so it will be easier for the programmers under you to make adaptions or improvements to this contribution if you wish.


Here are the installation instructions in 10 steps:

***************
A. The Database
***************

1.Create the following new table:

CREATE TABLE `related_products` (
`product_id` int(11) NOT NULL,
`related_product_id` int(11) NOT NULL,
PRIMARY KEY (`product_id`,`related_product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;


************
B. The Admin
************

2.Add the following sections of code to admin/model/catalog/product.php:

At the end of the function addProduct you see the line:

Code: Select all

		$this->cache->delete('product'); [approximately line 60]
just before that line add:

Code: Select all

		if (isset($data['related'])) {
			foreach ($data['related'] as $related_product_id) {
				$this->db->query("INSERT INTO related_products SET product_id = '" . (int)$product_id . "', related_product_id = '" . (int)$related_product_id . "'");
				$this->db->query("INSERT INTO related_products SET related_product_id = '" . (int)$product_id . "', product_id = '" . (int)$related_product_id . "'");
			}		
		}
At the end of the function editProduct you see the line:

Code: Select all

		$this->cache->delete('product'); [approximately line 133]
just before that line add:

Code: Select all

		$this->db->query("DELETE FROM related_products WHERE product_id = '" . (int)$product_id . "'");
		$this->db->query("DELETE FROM related_products WHERE related_product_id = '" . (int)$product_id . "'");
		
		if (isset($data['related'])) {
			foreach ($data['related'] as $related_product_id) {
				$this->db->query("INSERT INTO related_products SET product_id = '" . (int)$product_id . "', related_product_id = '" . (int)$related_product_id . "'");
				$this->db->query("INSERT INTO related_products SET related_product_id = '" . (int)$product_id . "', product_id = '" . (int)$related_product_id . "'");
			}		
		}
At the end of the function deleteProduct you see the line:

Code: Select all

		$this->cache->delete('product'); [approximately line 148]
just before that line add:

Code: Select all

		$this->db->query("DELETE FROM related_products WHERE product_id = '" . (int)$product_id . "'");
		$this->db->query("DELETE FROM related_products WHERE related_product_id = '" . (int)$product_id . "'");
Between the functions getProductDescriptions and getProductOptions [approximately at line 224] add:

Code: Select all

	public function CheckRelated($product_id1, $product_id2) {
		if (!$product_id1 || !$product_id2) {
			return FALSE;
		}
		$query = $this->db->query("select * from related_products WHERE product_id = '" . (int)$product_id1 . "' AND related_product_id = '" . (int)$product_id2 . "'");
		if ($query->rows) {
			return TRUE;
		}
		else {
			return FALSE;
		}
	}

3.Add the following sections of code to admin/controller/catalog/product.php:

After the line:

Code: Select all

		$this->data['entry_discount'] = $this->language->get('entry_discount'); [approximately line 381]
add the line:

Code: Select all

		$this->data['entry_related'] = $this->language->get('entry_related');
After the line:

Code: Select all

		$this->data['tab_category'] = $this->language->get('tab_category'); [approximately line 397]
add the line:

Code: Select all

		$this->data['tab_related'] = $this->language->get('tab_related');
At the end of the function getForm you see the line:

Code: Select all

 		$this->render(); [approximately line 623]
just before that line add:

Code: Select all

		$results = $this->model_catalog_product->getProducts();
		foreach ($results as $result) { 
			if (isset($this->request->get['product_id'])) {
				$related=$this->model_catalog_product->CheckRelated($this->request->get['product_id'], $result['product_id']);
			}
			else {
				$related=FALSE;
			}
      		$this->data['products'][] = array(
				'product_id' => $result['product_id'],
				'name'       => $result['name'],
				'model'      => $result['model'],
				'status'     => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
				'sort_order' => $result['sort_order'],
				'delete'     => in_array($result['product_id'], (array)@$this->request->post['delete']),
				'action'     => $action,
				'related'    => $related
			);
    	}


4.Add the following sections of code to admin/view/template/catalog/product_form.tpl:

In line 15, right after:

Code: Select all

<a tab="#tab_category"><?php echo $tab_category; ?></a>
and just before:

Code: Select all

</div>
add:

Code: Select all

<a tab="#tab_related"><?php echo $tab_related; ?></a>
Just before:

Code: Select all

</form>  [approximately line 550]
add:

Code: Select all

  <div id="tab_related" class="page"><?php echo $entry_related; ?><br />
    <div class="scrollbox">
		<?php $class = 'odd'; ?>
		<?php foreach ($products as $product) {  ?>
			<?php if (!isset($this->request->get['product_id']) || $this->request->get['product_id'] != $product['product_id']) { ?>
				<?php $class = ($class == 'even' ? 'odd' : 'even'); ?>
				<div class="<?php echo $class; ?>">
				<?php if ($product['related']) { ?>
					<input type="checkbox" name="related[]" value="<?php echo $product['product_id']; ?>" checked="checked" />
				<?php } else { ?>
					<input type="checkbox" name="related[]" value="<?php echo $product['product_id']; ?>" />
				<?php } ?>
				<?php echo $product['name']; ?>
				</div>
			<?php } ?>
		<?php } ?>
  </div></div>
If you have installed my previous contribution "Product Copy Feature, version 1.1.8", see http://forum.opencart.com/viewtopic.php?f=23&t=3315 then the line above:

Code: Select all

		<?php $class = 'odd'; ?>
should be changed to:

Code: Select all

		<?php $class = 'odd'; global $action; ?>
and the line above:

Code: Select all

			<?php if (!isset($this->request->get['product_id']) || $this->request->get['product_id'] != $product['product_id']) { ?>
should be changed to:

Code: Select all

			<?php if (!isset($this->request->get['product_id']) || $action->method=="insert" || $this->request->get['product_id'] != $product['product_id']) { ?>

5.Add the following code to admin/language/english/english.php:

After the line:

Code: Select all

$_['tab_update']              = 'Update'; [approximately line 70]
add:

Code: Select all

$_['tab_related']              = 'Related';

6.Add the following code to admin/language/english/catalog/product.php:

After the line:

Code: Select all

$_['entry_download']       = 'Downloads:'; [approximately line 39]
add:

Code: Select all

$_['entry_related']       = 'Recommended matching products:';

******************
C. The Store Front
******************

7.Add the following code to catalog/model/catalog/product.php:

Between the functions getPopularProducts and updateViewed [approximately at line 96] add:

Code: Select all

	public function getRelatedProducts($product_id) {
		$query = $this->db->query("SELECT * FROM (product p LEFT JOIN product_description pd ON (p.product_id = pd.product_id) LEFT JOIN image i ON (p.image_id = i.image_id)) JOIN related_products rp ON (p.product_id = rp.product_id) WHERE p.status = '1' AND pd.language_id = '" . (int)$this->language->getId() . "' AND p.date_available <= NOW() AND p.status = '1' AND rp.related_product_id = '" . (int)$product_id . "' ORDER BY p.viewed DESC");
		 	 
		$product = $query->rows;
		
		return $product;
	}

8.Add the following sections of code to catalog/controller/product/product.php:

After the line:

Code: Select all

			$this->data['text_note'] = $this->language->get('text_note'); [approximately line 121]
add the lines:

Code: Select all

			$this->data['text_related'] = $this->language->get('text_related');
			$this->data['text_no_related'] = $this->language->get('text_no_related');
After the line:

Code: Select all

			$this->model_catalog_product->updateViewed($this->request->get['product_id']); [approximately line 193]
add the code:

Code: Select all

			$this->data['products'] = array();

			$results = $this->model_catalog_product->getRelatedProducts($this->request->get['product_id']);
			
			foreach ($results as $result) {
				$rating = $this->model_catalog_review->getAverageRating($result['product_id']);	
			
				$this->data['products'][] = array(
					'name'   => $result['name'],
					'model'  => $result['model'],
					'rating' => $rating,
					'stars'  => sprintf($this->language->get('text_stars'), $rating),
					'thumb'  => HelperImage::resize($result['filename'], 120, 120),
					'price'  => $this->currency->format($this->tax->calculate($result['price'], $result['tax_class_id'], $this->config->get('config_tax'))),
					'href'   => $this->url->http('product/product&product_id=' . $result['product_id'])
				);
			}

9.Add the following code to catalog/view/template/product/product.tpl:

Just before the lines:

Code: Select all

</div> [approximately line 229]

<div class="bottom"></div> [approximately line 231]
add the following lines:

Code: Select all

  <div class="heading"><?php echo $text_related; ?></div>
  
  <?php if (!sizeof($products)) {
  	echo "<p>$text_no_related</p>";
  }?>
  
  <table class="list">

    <?php for ($i = 0; $i < sizeof($products); $i = $i + 4) { ?>

    <tr>

      <?php for ($j = $i; $j < ($i + 4); $j++) { ?>

      <td width="25%"><?php if (isset($products[$j])) { ?>

        <a href="<?php echo $products[$j]['href']; ?>"><img src="<?php echo $products[$j]['thumb']; ?>" title="<?php echo $products[$j]['name']; ?>" alt="<?php echo $products[$j]['name']; ?>" /></a><br />

        <a href="<?php echo $products[$j]['href']; ?>"><?php echo $products[$j]['name']; ?></a><br />

        <span style="color: #999; font-size: 11px;"><?php echo $products[$j]['model']; ?></span><br />

        <span style="color: #900; font-weight: bold;"><?php echo $products[$j]['price']; ?></span><br />

        <?php if ($products[$j]['rating']) { ?>

        <img src="catalog/view/image/stars_<?php echo $products[$j]['rating'] . '.png'; ?>" alt="<?php echo $products[$j]['stars']; ?>" />

        <?php } ?>

        <?php } ?></td>

      <?php } ?>

    </tr>

    <?php } ?>

  </table>

10.Add the following code to catalog/language/english/product/product.php:

After the line:

Code: Select all

$_['text_success']       = 'Thank you for your review. It has been submitted to the webmaster for approval.'; [approximately line 19]
add the lines:

Code: Select all

$_['text_related']       = 'Recommended matching products to buy';
$_['text_no_related']       = 'No recommendations';

I created the above list of instructions by tracing back the changes I made to get this working correctly. If this does not work for you, then please check that you made all the correct changes. If that does not solve the problem, then perhaps I made some mistake writing these instructions. In that case please let me know what problem or error you get, and I will correct it.


Questions, comments? Email me at henry@sturman.net

March 30, 2009

Newbie

Posts

Joined
Wed Feb 25, 2009 2:09 pm


Post by MarkF » Mon Mar 30, 2009 7:49 pm

I haven't tried this yet, but THANK YOU, THANK YOU, THANK YOU! 8)

1. for making the contrib
2. for taking the time to explain it in such detail
3. as a change to files rather than overwrite

Regards
Mark :)

New member

Posts

Joined
Tue Mar 17, 2009 9:54 pm

Post by make-oc » Wed Apr 08, 2009 6:01 am

Thanks Sturman, this is what I needed to get me started with OpenCart
As for
A problem with this is that it might not work well with a store with a large number of products (say a few hundred or more), because this will make the product list very long and therefore it may be difficult to scroll to the products you want to check.
I have a thought…

How about including the "Categories" navigation as such, only with all subcategories as well as all items added. Then use CSS/JS to make individual categories expand/collapse with "onClick" events.
This should be pretty straight forward and could be refined by using some Ajax. I'm thinking of a "drop zone" in which you can drag items from this navigation either individual items, multiple selected item or even a whole category. If "IDs" were to be added to the normal navigation, which should not hurt in any way, I think this should be doable.
(Ajax "drag & drop" examples on top of my head would be some shopping carts doing this, or for those of you who're using fabulous RoundCube webmailer will have a good idea of what I mean…)

Code: Select all

<ul>
  <li id="20"><a href="~/category&path=20">Desktops</a></li>
  <li id="18"><a href="~/category&path=18">Laptops & Notebooks</a></li>
  <li id="25"><a href="~/category&path=25">Components</a>
    <ul>
      <li id="25_28"><a href="~/category&path=25_28"><b>Monitors</b></a>
		<ul>
		  <li id="25_28--42">
		    <a href="~/product&path=25_28&product_id=42">Cinema 30"</a></li>
		  <li id="25_28--47">
		    <a href="~/product&path=25_28&product_id=47">HP LP3065</a></li>
		  <li id="25_28--33">
		    <a href="~/product&path=25_28&product_id=33">SyncMaster</a></li>
		</ul>
	</li>
      <li id="25_30"><a href="~/category&path=25_30">Printers</a></li>
      <li id="25_32"><a href="~/category&path=25_32">Web Cameras</a></li>
      <li id="25_29"><a href="~/category&path=25_29">Mice and Trackballs</a></li>
      <li id="25_31"><a href="~/category&path=25_31">Scanners</a></li>
    </ul>
  </li>
  <li id="17"><a href="~/category&path=17">Software</a></li>
  <li id="24"><a href="~/category&path=24"> Phones & PDAs</a></li>
  <li id="33"><a href="~/category&path=33">Cameras</a></li>
  <li id="34"><a href="~/category&path=34">MP3 Players</a></li>
</ul>
Now, as for editing this at a later point, I thing a simple list with items and "delete" link bellow the "drop zone" would be a simple solution as for user functionality.
Now, forgive me writing this after just having looked into the project for less then a day and let me know if you think this could not be done at all

Any thoughts are welcome and appreciated.

Cheers
make

New member

Posts

Joined
Tue Apr 07, 2009 2:17 pm

Post by make-oc » Thu Apr 16, 2009 2:37 pm

Here're my findings for opencart_v1.2.5…
3.Add the following sections of code to admin/controller/catalog/product.php:
There is no

Code: Select all

$this->data['tab_category'] = $this->language->get('tab_category'); [approximately line 397]
It seems like it's now:

Code: Select all

$this->data['tab_general'] = $this->language->get('tab_general');
Also the code

Code: Select all

          $results = $this->model_catalog_product->getProducts();
          foreach ($results as $result) {
             if (isset($this->request->get['product_id'])) {
                $related=$this->model_catalog_product->CheckRelated($this->request->get['product_id'], $result['product_id']);
             }
             else {
                $related=FALSE;
             }
                $this->data['products'][] = array(
                'product_id' => $result['product_id'],
                'name'       => $result['name'],
                'model'      => $result['model'],
                'status'     => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
                'sort_order' => $result['sort_order'],
                'delete'     => in_array($result['product_id'], (array)@$this->request->post['delete']),
                'action'     => $action,
                'related'    => $related
             );
           }
Has an undefined variable 'action' which throws a notice

Code: Select all

Notice: Undefined variable: action in ~/dpa/admin/controller/catalog/product.php on line 677
7.Add the following code to catalog/model/catalog/product.php:
The function didn't work due to some renaming in 1.2.5. DB tables or new structure
This is how it works:

Code: Select all

public function getRelatedProducts($product_id) {
		$query = $this->db->query("SELECT * FROM (product p LEFT JOIN product_description pd ON (p.product_id = pd.product_id) LEFT JOIN product_image i ON (p.image = i.product_image_id)) JOIN related_products rp ON (p.product_id = rp.product_id) WHERE p.status = '1' AND pd.language_id = '" . (int)$this->language->getId() . "' AND p.date_available <= NOW() AND p.status = '1' AND rp.related_product_id = '" . (int)$product_id . "' ORDER BY p.viewed DESC");
		 	 
		$product = $query->rows;
		
		return $product;
	}
Here some more Notices and Warnigs:
Notice: Undefined index: filename in ~catalog\controller\product\product.php on line 221

Warning: Cannot modify header information - headers already sent by (output started at ~catalog/controller/product/product.php:221) in ~system/library/response.php on line 65

Notice: Undefined variable: action in ~admin/controller/catalog/product.php on line 677

Warning: Cannot modify header information - headers already sent by (output started at ~admin/controller/catalog/product.php:677) in ~system/library/response.php on line 65
Just got it to work on a devsystem for now and have not done much testing.

Cheers

New member

Posts

Joined
Tue Apr 07, 2009 2:17 pm

Post by sturman » Sun Apr 19, 2009 5:38 am

I suppose some problems were caused by the fact my contribution was made for an older version (1.18).
Has an undefined variable 'action' which throws a notice
This may have to do with another change I made, sorry. I think you can just take out this part of the code:

Code: Select all

                'related'    => $related
(last change in my instruction 3.)

But I guess this issue is now no longer important, because I see the newest download has a related products feature included (don't know if this is an incorporation of my contribution above or another one):

http://forum.opencart.com/viewtopic.php ... ted#p18037

Henry

Newbie

Posts

Joined
Wed Feb 25, 2009 2:09 pm


Post by make-oc » Mon Apr 20, 2009 12:16 pm

Hmm well… unfortunatlly the new 1.2.6. "Related Products" function is not working both ways, like yours… http://forum.opencart.com/viewtopic.php ... 767#p18115

Cheers

New member

Posts

Joined
Tue Apr 07, 2009 2:17 pm

Post by thomassabo » Fri Nov 12, 2010 10:41 pm

hi ,sturman ,thanks a lot for your post ,It is helpful me !

my name is thomas sabo


Newbie

Posts

Joined
Thu Nov 04, 2010 1:58 pm

Who is online

Users browsing this forum: No registered users and 9 guests