Export (0) Print
Expand All

Editorial Review and Appeals in PHP

The following example shows how to find keywords that have been editorially rejected, print the reasons for the rejections, and then appeal the rejections using the following Campaign Management service operations.

This example has been developed and run within the environment described in Getting Started Using PHP with Bing Ads Services.

To download the PHP classes included in the sample, see PHP Production Classes for Bing Ads API.

System_CLiX_note Note

This example uses the UserName and Password elements for authentication. For Managing User Authentication with OAuth, replace the UserName and Password elements with the AuthenticationToken, which is your OAuth access token.

<?php
// You must pass the URL of the Campaign Management WSDL.

// Command line usage:
// php.exe AppealKeywordRejections wsdl
//

// Disable WSDL caching.

ini_set("soap.wsdl_cache_enabled", "0");
ini_set("soap.wsdl_cache_ttl", "0");

// API credentials.

$username = "<usernamegoeshere>";
$password = "<passwordgoeshere>";
$token = "<devtokengoeshere>";

// Advertiser's account ID and customer ID.

$accountId = <accountidgoeshere>;
$customerId = <customeridgoeshere>;

// The ID of the ad group that contains the keywords to appeal.
// Specify the ad group ID as a string.

$adGroupId = "<adgroupidgoeshere>";

// Campaign Management WSDL

$wsdl = $argv[1];


$ids = null;
$reasons = null;

try
{
    $proxy = new ClientProxy($username, $password, $token, $accountId, $customerId, $wsdl);

    $ids = GetKeywordsThatFailedReview($adGroupId, $proxy);

    if (null == $ids)
    {
        printf("Ad group %s does not contain keywords that failed editorial review.\n",
            $adGroupId);
    }
    else
    {
        $collections = GetReasonsForFailures($accountId, $ids, $proxy);

        foreach ($collections as $collection)
        {
            printf("Keyword %s failed editorial review for the following reasons.\n",
                $collection->AdOrKeywordId);

            foreach ($collection->Reasons->EditorialReason as $editorialReason)
            {
                print("  Reason code: " . $editorialReason->ReasonCode . "\n");
                print("  Term : " . $editorialReason->Term . "\n");
                print("  Location : " . $editorialReason->Location . "\n");
                print("  Publisher countries: ");

                $j = 1;
                $countryCount = count($editorialReason->PublisherCountries->string);
                foreach ($editorialReason->PublisherCountries->string as $country)
                {
                    printf("%s%s", $country, ($j++ < $countryCount) ? ", " : "\n");
                }
            }

            if ($collection->AppealStatus == AppealStatus::Appealable)
            {
                print("Appealing review.\n");
                $justification = "My justification text.";
                AppealReview(array($collection->AdOrKeywordId), $justification, $proxy);
            }
            else if ($collection->AppealStatus == AppealStatus::AppealPending)
            {
                print("Review is being appealed.\n");
            }
            else
            {
                print("Review cannot be appealed.\n");
            }

            print("\n");
        }
    }
}
catch (SoapFault $e)
{
    print $e->getMessage()."\n\n";
}
catch (Exception $e)
{
    if ($e->getPrevious())
    {
        ; // Ignore fault exceptions that we already caught.
    }
    else
    {
        print $e->getCode()." ".$e->getMessage()."\n\n";
        print $e->getTraceAsString()."\n\n";
    }
}

// Gets the IDs of the keywords that failed editorial review.

function GetKeywordsThatFailedReview($adGroupId, $proxy)
{
    $request = new GetKeywordsByEditorialStatusRequest();
    $response = null; // GetKeywordsByEditorialStatusResponse
    $ids = null;
    $count = 0;

    // Set the request information.

    $request->AdGroupId = $adGroupId;
    $request->EditorialStatus = KeywordEditorialStatus::Disapproved;

    try
    {
        $response = $proxy->GetService()->GetKeywordsByEditorialStatus($request);

        // Just need to return the IDs.

        if (!empty($response->Keywords->Keyword))
        {
            $ids = array();
            $count = count($response->Keywords->Keyword);

            for ($i = 0; $i < $count; $i++)
            {
                $ids[$i] = $response->Keywords->Keyword[$i]->Id;
            }
        }
    }
    catch (SoapFault $e)
    {
        print "GetKeywordsByEditorialStatus failed.\n";

        if (isset($e->detail->ApiFaultDetail))
        {
            print "ApiFaultDetail exception encountered\n";
            print "Tracking ID: ".$e->detail->ApiFaultDetail->TrackingId."\n";

            // Process any operation errors.

            if (!empty($e->detail->ApiFaultDetail->OperationErrors))
            {
                $errors = is_array($e->detail->ApiFaultDetail->OperationErrors->OperationError)
                        ? $e->detail->ApiFaultDetail->OperationErrors->OperationError
                        : array('OperationError' => $e->detail->ApiFaultDetail->OperationErrors->OperationError);

                foreach ($errors as $error)
                {
                    switch ($error->Code)
                    {
                        case 1201: //  CampaignServiceInvalidAdGroupId
                            print("The specified ad group ID is not valid.\n");
                            break;

                        default:
                            printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                                $error->ErrorCode, $error->Code, $error->Message, $error->Details);
                            break;
                    }
                }
            }

            // Process any batch errors.

            if (!empty($e->detail->ApiFaultDetail->BatchErrors))
            {
                $errors = is_array($e->detail->ApiFaultDetail->BatchErrors->BatchError)
                        ? $e->detail->ApiFaultDetail->BatchErrors->BatchError
                        : array('BatchError' => $e->detail->ApiFaultDetail->BatchErrors->BatchError);

                foreach ($errors as $error)
                {
                    switch ($error->Code)
                    {
                        default:
                            print "Batch error encountered for array index ".$error->Index.".\n";
                            printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                                $error->ErrorCode, $error->Code, $error->Message, $error->Details);
                            break;
                    }
                }
            }
        }
        elseif (isset($e->detail->AdApiFaultDetail))
        {
            print "AdApiFaultDetail exception encountered\n";
            print "Tracking ID: ".$e->detail->AdApiFaultDetail->TrackingId ."\n";

            // Process any system errors.

            $errors = is_array($e->detail->AdApiFaultDetail->Errors->AdApiError)
                    ? $e->detail->AdApiFaultDetail->Errors->AdApiError
                    : array('AdApiError' => $e->detail->AdApiFaultDetail->Errors->AdApiError);

            foreach ($errors as $error)
            {
                if (105 == $error->Code) //  InvalidCredentials
                {
                    print "The specified credentials are not valid or the account is inactive.\n";
                }
                else
                {
                    printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                        $error->ErrorCode, $error->Code, $error->Message, $error->Detail);
                }
            }
        }
        else
        {
            print $e->getCode()." ".$e->getMessage()."\n\n";

            // Output the last request/response.

            print "\nLast SOAP request/response:\n";
            print $proxy->GetWsdl()."\n";
            print $proxy->GetService()->__getLastRequest()."\n";
            print $proxy->GetService()->__getLastResponse()."\n";
        }

        throw new Exception("", 0, $e);
    }

    return $ids;
}

// Gets the reasons why the keywords failed editorial review.

function GetReasonsForFailures($accountId, $ids, $proxy)
{
    $request = new GetEditorialReasonsByIdsRequest();
    $response = null; // GetEditorialReasonsByIdsResponse

    // Set the request information.

    $request->AccountId = $accountId;
    $request->EntityIds = $ids;
    $request->EntityType = EntityType::Keyword;

    try
    {
        $response = $proxy->GetService()->GetEditorialReasonsByIds($request);
    }
    catch (SoapFault $e)
    {
        print "GetEditorialReasonsByIds failed.\n";

        if (isset($e->detail->ApiFaultDetail))
        {
            print "ApiFaultDetail exception encountered\n";
            print "Tracking ID: ".$e->detail->ApiFaultDetail->TrackingId."\n";

            // Process any operation errors.

            if (!empty($e->detail->ApiFaultDetail->OperationErrors))
            {
                $errors = is_array($e->detail->ApiFaultDetail->OperationErrors->OperationError)
                        ? $e->detail->ApiFaultDetail->OperationErrors->OperationError
                        : array('OperationError' => $e->detail->ApiFaultDetail->OperationErrors->OperationError);

                foreach ($errors as $error)
                {
                    switch ($error->Code)
                    {
                        case 1029: //
                            print("The customer ID header element cannot be null or empty.\n");
                            break;

                        default:
                            printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                                $error->ErrorCode, $error->Code, $error->Message, $error->Details);
                            break;
                    }
                }
            }

            // Process any batch errors.

            if (!empty($e->detail->ApiFaultDetail->BatchErrors))
            {
                $errors = is_array($e->detail->ApiFaultDetail->BatchErrors->BatchError)
                        ? $e->detail->ApiFaultDetail->BatchErrors->BatchError
                        : array('BatchError' => $e->detail->ApiFaultDetail->BatchErrors->BatchError);

                foreach ($errors as $error)
                {
                    switch ($error->Code)
                    {
                        default:
                            print "Batch error encountered for array index ".$error->Index.".\n";
                            printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                                $error->ErrorCode, $error->Code, $error->Message, $error->Details);
                            break;
                    }
                }
            }
        }
        elseif (isset($e->detail->AdApiFaultDetail))
        {
            print "AdApiFaultDetail exception encountered\n";
            print "Tracking ID: ".$e->detail->AdApiFaultDetail->TrackingId ."\n";

            // Process any system errors.

            $errors = is_array($e->detail->AdApiFaultDetail->Errors->AdApiError)
                    ? $e->detail->AdApiFaultDetail->Errors->AdApiError
                    : array('AdApiError' => $e->detail->AdApiFaultDetail->Errors->AdApiError);

            foreach ($errors as $error)
            {
                if (105 == $error->Code) //  InvalidCredentials
                {
                    print "The specified credentials are not valid or the account is inactive.\n";
                }
                else
                {
                    printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                        $error->ErrorCode, $error->Code, $error->Message, $error->Detail);
                }
            }
        }
        else
        {
            print $e->getCode()." ".$e->getMessage()."\n\n";

            // Output the last request/response.

            print "\nLast SOAP request/response:\n";
            print $proxy->GetWsdl()."\n";
            print $proxy->GetService()->__getLastRequest()."\n";
            print $proxy->GetService()->__getLastResponse()."\n";
        }

        throw new Exception("", 0, $e);
    }

    return $response->EditorialReasons->EditorialReasonCollection;
}

// Appeals the editorial rejections.

function AppealReview($ids, $justification, $proxy)
{
    $request = new AppealEditorialRejectionsRequest();
    $response = null;  // AppealEditorialRejectionsResponse

    // Set the request information.

    $request->EntityIds = $ids;
    $request->EntityType = EntityType::Keyword;
    $request->JustificationText = $justification;

    try
    {
        $response = $proxy->GetService()->AppealEditorialRejections($request);
    }
    catch (SoapFault $e)
    {
        print "AppealEditorialRejections failed.\n";

        if (isset($e->detail->ApiFaultDetail))
        {
            print "ApiFaultDetail exception encountered\n";
            print "Tracking ID: ".$e->detail->ApiFaultDetail->TrackingId."\n";

            // Process any operation errors.

            if (!empty($e->detail->ApiFaultDetail->OperationErrors))
            {
                $errors = is_array($e->detail->ApiFaultDetail->OperationErrors->OperationError)
                        ? $e->detail->ApiFaultDetail->OperationErrors->OperationError
                        : array('OperationError' => $e->detail->ApiFaultDetail->OperationErrors->OperationError);

                foreach ($errors as $error)
                {
                    switch ($error->Code)
                    {
                        default:
                            printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                                $error->ErrorCode, $error->Code, $error->Message, $error->Details);
                            break;
                    }
                }
            }

            // Process any batch errors.

            if (!empty($e->detail->ApiFaultDetail->BatchErrors))
            {
                $errors = is_array($e->detail->ApiFaultDetail->BatchErrors->BatchError)
                        ? $e->detail->ApiFaultDetail->BatchErrors->BatchError
                        : array('BatchError' => $e->detail->ApiFaultDetail->BatchErrors->BatchError);

                foreach ($errors as $error)
                {
                    switch ($error->Code)
                    {
                        default:
                            print "Batch error encountered for array index ".$error->Index.".\n";
                            printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                                $error->ErrorCode, $error->Code, $error->Message, $error->Details);
                            break;
                    }
                }
            }
        }
        elseif (isset($e->detail->AdApiFaultDetail))
        {
            print "AdApiFaultDetail exception encountered\n";
            print "Tracking ID: ".$e->detail->AdApiFaultDetail->TrackingId ."\n";

            // Process any system errors.

            $errors = is_array($e->detail->AdApiFaultDetail->Errors->AdApiError)
                    ? $e->detail->AdApiFaultDetail->Errors->AdApiError
                    : array('AdApiError' => $e->detail->AdApiFaultDetail->Errors->AdApiError);

            foreach ($errors as $error)
            {
                if (105 == $error->Code) //  InvalidCredentials
                {
                    print "The specified credentials are not valid or the account is inactive.\n";
                }
                else
                {
                    printf("Error code: %s (%d)\nMessage: %s\nDetail: %s\n",
                        $error->ErrorCode, $error->Code, $error->Message, $error->Detail);
                }
            }
        }
        else
        {
            print $e->getCode()." ".$e->getMessage()."\n\n";

            // Output the last request/response.

            print "\nLast SOAP request/response:\n";
            print $proxy->GetWsdl()."\n";
            print $proxy->GetService()->__getLastRequest()."\n";
            print $proxy->GetService()->__getLastResponse()."\n";
        }

        throw new Exception("", 0, $e);
    }
}

// Callbacks referenced in the SoapClient options.

// Converts long types found in SOAP responses to string types in PHP.

function from_long_xml($xmlFragmentString)
{
    return (string)strip_tags($xmlFragmentString);
}

// Converts PHP string types to long types in SOAP requests.

function to_long_xml($longVal)
{
    return '<long>' . $longVal . '</long>';
}

// Define the proxy class for the Campaign Management service.

Class ClientProxy
{
    private $username;
    private $password;
    private $developerToken;
    private $wsdlUrl;
    private $accountId;
    private $customerId;
    private $service;
    private $namespace;

    public function __construct($username, $password, $token, $accountId, $customerId, $wsdl)
    {
        $this->username = $username;
        $this->password = $password;
        $this->developerToken = $token;
        $this->accountId = $accountId;
        $this->customerId = $customerId;
        $this->wsdlUrl = $wsdl;
        $this->service = $this->GetProxy($wsdl);
    }

    public function GetAccountId() { return $this->accountId; }
    public function GetCustomerId() { return $this->customerId; }
    public function GetService() { return $this->service; }
    public function GetNamespace() { return $this->namespace; }
    public function GetWsdl() { return $this->wsdlUrl; }

    // This function gets the namespace from the WSDL, so you do
    // not have to hardcode it in the client.

    private function GetServiceNamespace($url)
    {
        $doc = new DOMDocument;
        $doc->Load($url);

        $xpath = new DOMXPath($doc);
        $query = "/*/@targetNamespace";

        $attrs = $xpath->query($query);

        // The query will return only one node in the node list.

        foreach($attrs as $attr)
        {
            $namespace = $attr->value;
        }

        return $namespace;
    }

    private function GetProxy($wsdl)
    {
        $this->namespace = $this->GetServiceNamespace($wsdl);

        // Define the SOAP headers. Customer ID is required
        // to get editorial reasons.

        $headers = array();

        $headers[] = new SoapHeader(
            $this->namespace,
            'CustomerAccountId',
            $this->accountId
            );

        $headers[] = new SoapHeader(
            $this->namespace,
            'CustomerId',
            $this->customerId
            );

        $headers[] = new SoapHeader(
            $this->namespace,
            'DeveloperToken',
            $this->developerToken
            );

        $headers[] = new SoapHeader(
            $this->namespace,
            'UserName',
            $this->username
            );

        $headers[] = new SoapHeader(
            $this->namespace,
            'Password',
            $this->password
            );

        // By default, PHP does not return single item arrays as an array type.
        // To force PHP to always return an array for an array type in the
        // response, specify the SOAP_SINGLE_ELEMENT_ARRAYS feature.

        $options = array(
            'trace' => TRUE,
            'exceptions' => TRUE,
            'features' => SOAP_SINGLE_ELEMENT_ARRAYS,

            // Map long type to string type. For details, see
            // from_long_xml and to_long_xml callbacks.

            'typemap' => array(
                array(
                'type_ns' => 'http://www.w3.org/2001/XMLSchema',
                'type_name' => 'long',
                'to_xml' => 'to_long_xml',
                'from_xml' => 'from_long_xml'
                ),
            )
        );

        $proxy = @new SOAPClient($this->wsdlUrl, $options);

        $proxy->__setSoapHeaders($headers);

        return $proxy;
    }
}

// Defines the Campaign Management value set classes used in this example.

final class AppealStatus
{
    const Appealable = 'Appealable';
    const AppealPending = 'AppealPending';
    const NotAppealable = 'NotAppealable';
}

final class EntityType
{
    const Campaign = 'Campaign';
    const AdGroup = 'AdGroup';
    const Target = 'Target';
    const Ad = 'Ad';
    const Keyword = 'Keyword';
    const AdExtension = 'AdExtension';
}

final class KeywordEditorialStatus
{
    const Active = 'Active';
    const Disapproved = 'Disapproved';
    const Inactive = 'Inactive';
}

final class KeywordStatus
{
    const Active = 'Active';
    const Paused = 'Paused';
    const Deleted = 'Deleted';
    const Inactive = 'Inactive';
}


// Defines the Campaign Management error objects that the
// operations can return.

final class AdApiError
{
    public $Code;
    public $Detail;
    public $ErrorCode;
    public $Message;
}

final class BatchError
{
    public $Code;
    public $Details;
    public $ErrorCode;
    public $Index;
    public $Message;
}

final class EditorialError
{
    public $Appealable;
    public $Code;
    public $DisapprovedText;
    public $ErrorCode;
    public $Index;
    public $Message;
    public $PublisherCountry;
}

final class OperationError
{
    public $Code;
    public $Details;
    public $ErrorCode;
    public $Message;
}

class ApplicationFault
{
    public $TrackingId;
}

final class ApiFaultDetail extends ApplicationFault
{
    public $BatchErrors;
    public $OperationErrors;
}

final class AdApiFaultDetail extends ApplicationFault
{
    public $Errors;
}

final class EditorialApiFaultDetail extends ApplicationFault
{
    public $BatchErrors;
    public $EditorialErrors;
    public $OperationErrors;
}


// Defines the Campaign Management data objects used in this example.

final class EditorialReason
{
    public $Location;
    public $PublisherCountries;
    public $ReasonCode;
    public $Term;
}

final class EditorialReasonCollection
{
    public $AdOrKeywordId;
    public $AppealStatus;
    public $Reasons;
}

final class Keyword
{
    public $BroadMatchBid;
    public $ContentMatchBid;
    public $EditorialStatus;
    public $ExactMatchBid;
    public $Id;
    public $NegativeKeywords;
    public $Param1;
    public $Param2;
    public $Param3;
    public $PhraseMatchBid;
    public $Status;
    public $Text;
}


// Defines the Campaign Management operations used in this example.

final class GetKeywordsByEditorialStatusRequest
{
    public $AdGroupId;
    public $EditorialStatus;
}

final class GetKeywordsByEditorialStatusResponse
{
    public $Keywords;
}

final class GetEditorialReasonsByIdsRequest
{
    public $AccountId;
    public $EntityIds;
    public $EntityType;
}

final class GetEditorialReasonsByIdsResponse
{
    public $EditorialReasons;
}

final class AppealEditorialRejectionsRequest
{
    public $EntityIds;
    public $EntityType;
    public $JustificationText;
}

final class AppealEditorialRejectionsResponse
{
}

?>

Community Additions

Show:
© 2014 Microsoft