Jump to content

Adding custom field value to Product Name


izghitu

Recommended Posts

Hi,

 

I have a product that has some custom fields for the customer to enter after the purchase. Is there any way to make the product name append one of the custom fields as its name at the time when I view the product list for either the client or for all clients?

For example the product name is: Server monitoring. One the custom fields the client uses is IP: 1.2.3.4. After the custom field is entered by the client the product name should show: Server monitoring(1.2.3.4) or whatever text/format I need on the page where I view the product list or in the drop down menu where I select the product to view for the particular client.

Please let me know

Thanks in advance.

Link to comment
Share on other sites

Just an update. I was able to implement this for the client area only like this:

includes/hooks/service_customfields.php

<?php
use WHMCS\Database\Capsule as DB;
if (!defined("WHMCS")) {
    die("This file cannot be accessed directly");
}

add_hook('ClientAreaPageProductsServices', 1, function($vars) {

    $fieldId = 1; //Custom field id
    if (isset($vars['services'])) {
        $csVals = [];
        foreach ($vars['services'] as $service) {

            $fieldVal = '';
            $data = DB::table('tblcustomfieldsvalues AS t1')
            ->leftJoin('tblcustomfields AS t2', 't1.fieldid', '=', 't2.id')
            ->select('t1.value')
            ->where('t2.id', $fieldId)->where('t2.type', 'product')->where('t1.relid', $service['id'])
            ->first();
            if (!is_null($data)) {
                $fieldVal = $data->value;
            }
            $csVals[$service['id']] = $fieldVal;

        }

        return ['customFields' => $csVals];
    }
});

templates/six/clientareaproducts.tpl -  I have added  "- {$customFields[$service.id]}" right after {$service.product}

...
                    <td><strong>{$service.product} - {$customFields[$service.id]}</strong>{if $service.domain}<br /><a href="http://{$service.domain}" target="_blank">{$service.domain}</a>{/if}</td>
...

 

But I am still unable to apply the same logic to the admin area interface where I list the products. I even searched for the "service.product" string in all whmcs files and did not find any except those in the client area that I already applied this to.

Does anyone know how do I apply the same to the admin interface?

Please help. Thanks!

Link to comment
Share on other sites

47 minutes ago, izghitu said:

Just an update. I was able to implement this for the client area only

firstly, well done for posting your code - it's sad how many users don't share their own solutions. thanks.png

47 minutes ago, izghitu said:

But I am still unable to apply the same logic to the admin area interface where I list the products. I even searched for the "service.product" string in all whmcs files and did not find any except those in the client area that I already applied this to.

are you thinking specifically of the encrypted clientservices.php page? might be awkward to do that... unless you wrote a hook to change the actual names of the services in the database and then you wouldn't have to worry about tweaking the output in the client/admin areas.

the other way in might be the clientsummary.tpl template... which on the frontpage shows the products for an individual client... that at least gives you an identifiable array that you can manipulate with a hook to tweak the product name.

Link to comment
Share on other sites

5 minutes ago, izghitu said:

The WHMCS support guys just told me there's no way to do this for the admin interface and encouraged me to write a Feature request which I will do. 

three points with this...

  1. just because Support say 'there's no way to do this', never assume that they're correct - often it's true, but not always... and I speak from personal experience as many years ago, after being told by Support that something couldn't be done, I solved it myself in about 30 minutes... to be fair, depending upon what exactly you asked them, i'd be inclined to think that modifying the services page (as you envisage) using a hook wouldn't be possible.... though i'd stand by that either modifying the client summary tables would be possible, as would updating the database directly to change the product name.
  2. be aware that feature requests, however trivial you may think them to be, will take years before being completed by WHMCS - that's just a fact.
  3. if you submit a feature request, it might be useful to add a link to the request in this thread in case anyone reads this thread in the future... otherwise, it might not be easy to find!
Link to comment
Share on other sites

On 9/20/2018 at 7:35 PM, brian! said:

three points with this...

  1. just because Support say 'there's no way to do this', never assume that they're correct - often it's true, but not always... and I speak from personal experience as many years ago, after being told by Support that something couldn't be done, I solved it myself in about 30 minutes... to be fair, depending upon what exactly you asked them, i'd be inclined to think that modifying the services page (as you envisage) using a hook wouldn't be possible.... though i'd stand by that either modifying the client summary tables would be possible, as would updating the database directly to change the product name.
  2. be aware that feature requests, however trivial you may think them to be, will take years before being completed by WHMCS - that's just a fact.
  3. if you submit a feature request, it might be useful to add a link to the request in this thread in case anyone reads this thread in the future... otherwise, it might not be easy to find!

So I chose the hooks way to get the same implemented into the admin interface. Below is the hook to be used to make it display the required custom field's value along with the product name:

<?php

if ( !defined('WHMCS')) {
    header("Location: ../../index.php");
}

use WHMCS\Database\Capsule;

// Please set custom field id here.
$customfieldId = 1;

if($_REQUEST['getAll'] == '1')
{
    if($_REQUEST['userid'] != '')
{
    $all    = Capsule::table('tblcustomfieldsvalues')
            ->join('tblhosting', 'tblcustomfieldsvalues.relid', '=', 'tblhosting.id')
            ->where('fieldid', $customfieldId )
            ->where('userid', $_REQUEST['userid'] )
            ->get();
}else
{
    $all    = Capsule::table('tblcustomfieldsvalues')->where('fieldid', $customfieldId)->get();
}
    
    $all    = json_decode( json_encode( $all ) , true );
    $newAll = [];

    foreach($all as $a)
    {
        $newAll[$a['relid']] = $a['value'];
    }

    ob_clean();
    echo json_encode(array( "all" => $newAll ));
    die;
}

add_hook('AdminAreaPage', 1, function($vars) {

    if( $vars['filename'] == 'clientsservices' )
    {
        echo '<script>
            document.addEventListener("DOMContentLoaded", function(){ 
            $.post(window.location,
            { getAll: 1 },
            function(data, status){

            var data = JSON.parse(data);
            var first = [];

            setInterval(function() {

                var x = document.querySelectorAll("[data-value]");

                for (var i = 1; i < x.length ; i++) {
                 
                    if(typeof first[x[i].dataset.value] === "undefined")
                    {
                        first[x[i].dataset.value] = x[i].innerText;
                    }
                    if(typeof data.all[x[i].dataset.value] !== "undefined" && data.all[x[i].dataset.value] != "")
                    {
                        x[i].innerText = first[x[i].dataset.value] + " - " + data.all[x[i].dataset.value];
                    } 
                }     
              }, 100);
                });   
        });
        </script>';
    }

    if( $vars['filename'] == 'clientshostinglist')
    {
        echo '<script>
        document.addEventListener("DOMContentLoaded", function(){ 
            $.post(window.location,
            { getAll: 1 },
            function(data, status){
                var data = JSON.parse(data);
                var table = document.getElementById("sortabletbl1");
                var rows = table.rows;
                for (var i = 1; i < rows.length ; i++) {
                    if(data.all[rows[i].cells[1].innerText] != null && data.all[rows[i].cells[1].innerText] != "")
                    {
                        rows[i].cells[2].innerHTML = rows[i].cells[2].innerHTML+ " - " + data.all[rows[i].cells[1].innerText];
                    }
                }
            });
        });
        </script>';
    }
});

The only thing that needs to be setup is the $customfieldId inside the hook.

 

Hope this is useful to someone.

Thanks!

Link to comment
Share on other sites

  • 2 months later...

I wanted to give back for this brilliant piece of code IZGHITU provided that helped me tremendously. The problem I faced is that I needed this coded to work for multiple products utilizing the same custom field name and his code snippet was hard coded for one product. I am not that great but a couple hours later it is dynamic for multiple products. Just change the 'fieldname' in the first query to match your needs. You can also change 'fieldtype' and 'type' to further suit your needs. If anyone can provide constructive criticism I'd appreciate it but for now this works for me and for many products instead of just one even if you delete a product and add it again since it will find the id for us now. For me I wanted to see the Location Name in the admin area.

 

if($_REQUEST['getAll'] == '1')
{
    $newAll = [];
    $gotCustomFieldID = [];

    // Get fieldid for 'Location Name' for every product
    // Change 'Location Name' to suit your custom field needed
    $getCustomFieldID = Capsule::table('tblcustomfields')
        ->select('id')
        ->where('tblcustomfields.type','product')
        ->where('tblcustomfields.fieldname','Location Name')
        ->where('tblcustomfields.fieldtype','text')
        ->get();
        foreach ($getCustomFieldID as $data)
        {
            $gotCustomFieldID[] = $data->id;
        }

    foreach ($gotCustomFieldID as $customFieldID)
    {
        $all = Capsule::table('tblcustomfieldsvalues')
            ->join('tblhosting', 'tblcustomfieldsvalues.relid', '=', 'tblhosting.id')
            ->where('fieldid', $customFieldID )
            ->where('userid', $_REQUEST['userid'] )
            ->get();

        $all    = json_decode( json_encode( $all ) , true );
        foreach($all as $a)
        {
            $newAll[$a['relid']] = $a['value'];
        }
    }

    ob_clean();
    echo json_encode(array( "all" => $newAll ));
    die;

}

 

Untitled.png

Link to comment
Share on other sites

  • 1 month later...
On 12/11/2018 at 1:26 PM, ewsoares said:

I wanted to give back for this brilliant piece of code IZGHITU provided that helped me tremendously. The problem I faced is that I needed this coded to work for multiple products utilizing the same custom field name and his code snippet was hard coded for one product. I am not that great but a couple hours later it is dynamic for multiple products. Just change the 'fieldname' in the first query to match your needs. You can also change 'fieldtype' and 'type' to further suit your needs. If anyone can provide constructive criticism I'd appreciate it but for now this works for me and for many products instead of just one even if you delete a product and add it again since it will find the id for us now. For me I wanted to see the Location Name in the admin area.

 


if($_REQUEST['getAll'] == '1')
{
    $newAll = [];
    $gotCustomFieldID = [];

    // Get fieldid for 'Location Name' for every product
    // Change 'Location Name' to suit your custom field needed
    $getCustomFieldID = Capsule::table('tblcustomfields')
        ->select('id')
        ->where('tblcustomfields.type','product')
        ->where('tblcustomfields.fieldname','Location Name')
        ->where('tblcustomfields.fieldtype','text')
        ->get();
        foreach ($getCustomFieldID as $data)
        {
            $gotCustomFieldID[] = $data->id;
        }

    foreach ($gotCustomFieldID as $customFieldID)
    {
        $all = Capsule::table('tblcustomfieldsvalues')
            ->join('tblhosting', 'tblcustomfieldsvalues.relid', '=', 'tblhosting.id')
            ->where('fieldid', $customFieldID )
            ->where('userid', $_REQUEST['userid'] )
            ->get();

        $all    = json_decode( json_encode( $all ) , true );
        foreach($all as $a)
        {
            $newAll[$a['relid']] = $a['value'];
        }
    }

    ob_clean();
    echo json_encode(array( "all" => $newAll ));
    die;

}

 

Untitled.png

I was able to use the initial code shared by "izghitu" but new code shared by "ewsoares" did not work to me.
I used same way to use the quoted code like the initial code.

It will be very great if someone advise me to use it in right way.

Link to comment
Share on other sites

Off the top of my head I think you may not have created your custom fields or added the custom field name into the code. See below and let us know if this was it to help someone else who stumbles upon this thread please. 

->where('tblcustomfields.type','product')
        ->where('tblcustomfields.fieldname','Location Name')  <--- The name you named your custom field that you added to a PRODUCT. See line above as it is PRODUCT type as opposed to I think CUSTOMER custom fields.
        ->where('tblcustomfields.fieldtype','text')  <--- The type of custom field you added, currently set to text field, can use drop down, etc. Check DataBase to be sure what these field types are called.
Link to comment
Share on other sites

And remember my posted code only replaces the following portion of what IZGHITU posted. You still need the whole code he posted replacing only below with what I posted

if($_REQUEST['getAll'] == '1')
{
    if($_REQUEST['userid'] != '')
{
    $all    = Capsule::table('tblcustomfieldsvalues')
            ->join('tblhosting', 'tblcustomfieldsvalues.relid', '=', 'tblhosting.id')
            ->where('fieldid', $customfieldId )
            ->where('userid', $_REQUEST['userid'] )
            ->get();
}else
{
    $all    = Capsule::table('tblcustomfieldsvalues')->where('fieldid', $customfieldId)->get();
}
    
    $all    = json_decode( json_encode( $all ) , true );
    $newAll = [];

    foreach($all as $a)
    {
        $newAll[$a['relid']] = $a['value'];
    }

    ob_clean();
    echo json_encode(array( "all" => $newAll ));
    die;
}
Link to comment
Share on other sites

  • 2 weeks later...
On 2/3/2019 at 12:53 PM, ewsoares said:

And remember my posted code only replaces the following portion of what IZGHITU posted. You still need the whole code he posted replacing only below with what I posted


if($_REQUEST['getAll'] == '1')
{
    if($_REQUEST['userid'] != '')
{
    $all    = Capsule::table('tblcustomfieldsvalues')
            ->join('tblhosting', 'tblcustomfieldsvalues.relid', '=', 'tblhosting.id')
            ->where('fieldid', $customfieldId )
            ->where('userid', $_REQUEST['userid'] )
            ->get();
}else
{
    $all    = Capsule::table('tblcustomfieldsvalues')->where('fieldid', $customfieldId)->get();
}
    
    $all    = json_decode( json_encode( $all ) , true );
    $newAll = [];

    foreach($all as $a)
    {
        $newAll[$a['relid']] = $a['value'];
    }

    ob_clean();
    echo json_encode(array( "all" => $newAll ));
    die;
}

Actually, the original Hook and .tpl edit is fine in mechanism, what I need is to use field name instead of field ID so I can use a general field name for all products (like product serial number) and have it in all customer products not just for a specific filed of specific product.

Thank for the reply!

Link to comment
Share on other sites

I finally found the way!!

The original code is doing below query:

SELECT *
FROM tblcustomfieldsvalues AS t1
LEFT JOIN tblcustomfields AS t2 ON t1.fieldid = t2.id
WHERE t2.id = 2 #Field id
AND t2.type = 'product'
AND t1.relid = 9 #service id

 

So on the query part we dont need to do anything

but in retrieving part, we can have a change

I have changed:
 ->where('t2.id', $fieldId)->where('t2.type', 'product')->where('t1.relid', $service['id'])
to:
->where('t2.fieldname', $fieldId)->where('t2.type', 'product')->where('t1.relid', $service['id'])

and

from:
$fieldId = 4; //Custom field id
to:
$fieldId = 'Field Name'; //Custom field name

Now, in the product list of clientarea, we have the custom field value on all products, no matter of field ID, we only need to have a custom field with same name on all products.

so the new code for hook file will be:

 

<?php
use WHMCS\Database\Capsule as DB;
if (!defined("WHMCS")) {
    die("This file cannot be accessed directly");
}

add_hook('ClientAreaPageProductsServices', 1, function($vars) {

    $fieldId = 'Field Name'; //Custom field name
    if (isset($vars['services'])) {
        $csVals = [];
        foreach ($vars['services'] as $service) {

            $fieldVal = '';
            $data = DB::table('tblcustomfieldsvalues AS t1')
            ->leftJoin('tblcustomfields AS t2', 't1.fieldid', '=', 't2.id')
            ->select('t1.value')
            ->where('t2.fieldname', $fieldId)->where('t2.type', 'product')->where('t1.relid', $service['id'])
            ->first();
            if (!is_null($data)) {
                $fieldVal = $data->value;
            }
            $csVals[$service['id']] = $fieldVal;

        }

        return ['customFields' => $csVals];
    }
});

 

the rest is same as "izghitu" initial codes:

On 9/18/2018 at 9:34 PM, izghitu said:

Just an update. I was able to implement this for the client area only like this:

includes/hooks/service_customfields.php


<?php
use WHMCS\Database\Capsule as DB;
if (!defined("WHMCS")) {
    die("This file cannot be accessed directly");
}

add_hook('ClientAreaPageProductsServices', 1, function($vars) {

    $fieldId = 1; //Custom field id
    if (isset($vars['services'])) {
        $csVals = [];
        foreach ($vars['services'] as $service) {

            $fieldVal = '';
            $data = DB::table('tblcustomfieldsvalues AS t1')
            ->leftJoin('tblcustomfields AS t2', 't1.fieldid', '=', 't2.id')
            ->select('t1.value')
            ->where('t2.id', $fieldId)->where('t2.type', 'product')->where('t1.relid', $service['id'])
            ->first();
            if (!is_null($data)) {
                $fieldVal = $data->value;
            }
            $csVals[$service['id']] = $fieldVal;

        }

        return ['customFields' => $csVals];
    }
});

templates/six/clientareaproducts.tpl -  I have added  "- {$customFields[$service.id]}" right after {$service.product}


...
                    <td><strong>{$service.product} - {$customFields[$service.id]}</strong>{if $service.domain}<br /><a href="http://{$service.domain}" target="_blank">{$service.domain}</a>{/if}</td>
...

 

But I am still unable to apply the same logic to the admin area interface where I list the products. I even searched for the "service.product" string in all whmcs files and did not find any except those in the client area that I already applied this to.

Does anyone know how do I apply the same to the admin interface?

Please help. Thanks!

 

Link to comment
Share on other sites

  • WHMCS ChrisD locked and unlocked this topic
  • 4 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated