google 广告系列 、 预算、 账号列表 2
This commit is contained in:
parent
d771a95c42
commit
ab8e40d9cf
251
app/util/ArgumentNames.php
Normal file
251
app/util/ArgumentNames.php
Normal file
@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace app\util;
|
||||
|
||||
/**
|
||||
* Provides argument name constants for examples.
|
||||
*/
|
||||
final class ArgumentNames
|
||||
{
|
||||
public const ACCESS_ROLE = 'accessRole';
|
||||
public const ADJUSTMENT_DATE_TIME = 'adjustmentDateTime';
|
||||
public const ADJUSTMENT_TYPE = 'adjustmentType';
|
||||
public const ADVERTISER_UPLOAD_DATE_TIME = 'advertiserUploadDateTime';
|
||||
public const AD_ID = 'adId';
|
||||
public const AD_GROUP_ID = 'adGroupId';
|
||||
public const AD_GROUP_IDS = 'adGroupIds';
|
||||
public const AD_PERSONALIZATION_CONSENT = 'adPersonalizationConsent';
|
||||
public const AD_USER_DATA_CONSENT = 'adUserDataConsent';
|
||||
public const ASSET_GROUP_ID = 'assetGroupId';
|
||||
public const ATTRIBUTE_VALUE = 'attributeValue';
|
||||
public const AUDIENCE_ID = 'audienceId';
|
||||
public const BASE_CAMPAIGN_ID = 'baseCampaignId';
|
||||
public const BID_MODIFIER_VALUE = 'bidModifierValue';
|
||||
public const BILLING_SETUP_ID = 'billingSetupId';
|
||||
public const BRIDGE_MAP_VERSION_ID = 'bridgeMapVersionId';
|
||||
public const BUSINESS_ACCOUNT_IDENTIFIER = 'businessAccountIdentifier';
|
||||
public const BUSINESS_PROFILE_LOCATION = 'businessProfileLocation';
|
||||
public const BUSINESS_NAME = 'businessName';
|
||||
public const CALL_START_DATE_TIME = 'callStartDateTime';
|
||||
public const CALLER_ID = 'callerId';
|
||||
public const CALLOUT_TEXT = 'calloutText';
|
||||
public const CAMPAIGN_BUDGET_ID = 'campaignBudgetId';
|
||||
public const CAMPAIGN_EXPERIMENT_ID = 'campaignExperimentId';
|
||||
public const CAMPAIGN_ID = 'campaignId';
|
||||
public const CAMPAIGN_IDS = 'campaignIds';
|
||||
public const CARRIER_COUNTRY_CODE = 'carrierCountryCode';
|
||||
public const CHAIN_ID = 'chainId';
|
||||
public const CHECK_IN_DAY_CRITERION_ID = 'checkInDayCriterionId';
|
||||
public const CONVERSION_ACTION_ID = 'conversionActionId';
|
||||
public const CONVERSION_ACTION_IDS = 'conversionActionIds';
|
||||
public const CONVERSION_CUSTOM_VARIABLE_ID = 'conversionCustomVariableId';
|
||||
public const CONVERSION_CUSTOM_VARIABLE_VALUE = 'conversionCustomVariableValue';
|
||||
public const CONVERSION_DATE_TIME = 'conversionDateTime';
|
||||
public const CONVERSION_RATE_MODIFIER = "conversionRateModifier";
|
||||
public const CONVERSION_VALUE = 'conversionValue';
|
||||
public const COUNTRY_CODE = 'countryCode';
|
||||
public const CPC_BID_CEILING_MICRO_AMOUNT = 'cpcBidCeilingMicroAmount';
|
||||
public const CPC_BID_MICRO_AMOUNT = 'cpcBidMicroAmount';
|
||||
public const CRITERION_ID = 'criterionId';
|
||||
public const CURRENCY_CODE = 'currencyCode';
|
||||
public const CUSTOMER_ID = 'customerId';
|
||||
public const CUSTOMIZER_ATTRIBUTE_NAME = 'customizerAttributeName';
|
||||
public const CUSTOM_KEY = 'customKey';
|
||||
public const DRAFT_ID = 'draftId';
|
||||
public const EMAIL_ADDRESS = 'emailAddress';
|
||||
public const END_DATE_TIME = "endDateTime";
|
||||
public const EXTERNAL_ID = 'externalId';
|
||||
public const FEED_ID = 'feedId';
|
||||
public const FEED_ITEM_ID = 'feedItemId';
|
||||
public const FEED_ITEM_IDS = 'feedItemIds';
|
||||
public const FEED_ITEM_SET_ID = 'feedItemSetId';
|
||||
public const FINAL_URL = 'finalUrl';
|
||||
public const FLIGHT_PLACEHOLDER_FIELD_NAME = 'flightPlaceholderFieldName';
|
||||
public const FREE_FORM_KEYWORD_TEXT = 'freeFormKeywordText';
|
||||
public const GBRAID = 'gbraid';
|
||||
public const GCLID = 'gclid';
|
||||
public const GEO_TARGET_CONSTANT_ID = 'geoTargetConstantId';
|
||||
public const BUSINESS_PROFILE_ACCESS_TOKEN = 'businessProfileAccessToken';
|
||||
public const BUSINESS_PROFILE_EMAIL = 'businessProfileEmail';
|
||||
public const HOTEL_CENTER_ACCOUNT_ID = 'hotelCenterAccountId';
|
||||
public const IMAGE_ASSET_ID = 'imageAssetId';
|
||||
public const ITEM_ID = 'itemId';
|
||||
public const KEYWORD_PLAN_ID = 'keywordPlanId';
|
||||
public const KEYWORD_TEXT = 'keywordText';
|
||||
public const KEYWORD_TEXTS = 'keywordTexts';
|
||||
public const LABEL_ID = "labelId";
|
||||
public const LANGUAGE_CODE = 'languageCode';
|
||||
public const LANGUAGE_ID = 'languageId';
|
||||
public const LANGUAGE_NAME = 'languageName';
|
||||
public const LOCALE = 'locale';
|
||||
public const LOCATION_ID = 'locationId';
|
||||
public const LOCATION_IDS = 'locationIds';
|
||||
public const LOCATION_NAMES = 'locationNames';
|
||||
public const LOGIN_CUSTOMER_ID = 'loginCustomerId';
|
||||
public const MANAGER_CUSTOMER_ID = 'managerCustomerId';
|
||||
public const MARKETING_IMAGE_ASSET_ID = 'marketingImageAssetId';
|
||||
public const MERCHANT_CENTER_ACCOUNT_ID = 'merchantCenterAccountId';
|
||||
public const NAME_PREFIX = 'namePrefix';
|
||||
public const OFFLINE_USER_DATA_JOB_ID = 'offlineUserDataJobId';
|
||||
public const OFFLINE_USER_DATA_JOB_TYPE = 'offlineUserDataJobType';
|
||||
public const OMIT_UNSELECTED_RESOURCE_NAMES = 'omitUnselectedResourceNames';
|
||||
public const ORDER_ID = 'orderId';
|
||||
public const OUTPUT_FILE_PATH = 'outputFilePath';
|
||||
public const PAGE_URL = 'pageUrl';
|
||||
public const PARTNER_ID = 'partnerId';
|
||||
public const PAYMENTS_ACCOUNT_ID = 'paymentsAccountId';
|
||||
public const PAYMENTS_PROFILE_ID = 'paymentsProfileId';
|
||||
public const PERCENT_CPC_BID_MICRO_AMOUNT = 'percentCpcBidMicroAmount';
|
||||
public const PHONE_COUNTRY = 'phoneCountry';
|
||||
public const PHONE_NUMBER = 'phoneNumber';
|
||||
public const PLACE_ID = 'placeId';
|
||||
public const RECOMMENDATION_ID = 'recommendationId';
|
||||
public const RESTATEMENT_VALUE = 'restatementValue';
|
||||
public const CREATE_DEFAULT_LISTING_GROUP = 'createDefaultListingGroup';
|
||||
public const DELETE_EXISTING_FEEDS = 'deleteExistingFeeds';
|
||||
public const REPLACE_EXISTING_TREE = 'replaceExistingTree';
|
||||
public const QUANTITY = 'quantity';
|
||||
public const RUN_JOB = 'runJob';
|
||||
public const SALES_COUNTRY = 'salesCountry';
|
||||
public const SITELINK_TEXT = 'sitelinkText';
|
||||
public const SQUARE_MARKETING_IMAGE_ASSET_ID = 'squareMarketingImageAssetId';
|
||||
public const START_DATE_TIME = "startDateTime";
|
||||
public const THINGS_TO_DO_CENTER_ACCOUNT_ID = 'thingsToDoCenterAccountId';
|
||||
public const USER_AGENT = 'userAgent';
|
||||
public const USER_LIST_ID = 'userListId';
|
||||
public const USER_LIST_IDS = 'userListIds';
|
||||
public const WBRAID = 'wbraid';
|
||||
|
||||
public static $ARGUMENTS_TO_DESCRIPTIONS = [
|
||||
self::ACCESS_ROLE => 'The user access role',
|
||||
self::ADJUSTMENT_DATE_TIME => 'The adjustment date time',
|
||||
self::ADJUSTMENT_TYPE => 'The adjustment type',
|
||||
self::ADVERTISER_UPLOAD_DATE_TIME => 'The advertiser upload date time',
|
||||
self::AD_ID => 'The ad ID',
|
||||
self::AD_GROUP_ID => 'The ad group ID',
|
||||
self::AD_GROUP_IDS => 'The ad group IDs',
|
||||
self::AD_PERSONALIZATION_CONSENT => 'The ad personalization consent, e.g., GRANTED',
|
||||
self::AD_USER_DATA_CONSENT => 'The ad user data consent, e.g., GRANTED',
|
||||
self::ASSET_GROUP_ID => 'The asset group ID',
|
||||
self::ATTRIBUTE_VALUE => 'The attribute value',
|
||||
self::AUDIENCE_ID => 'The audience ID',
|
||||
self::BASE_CAMPAIGN_ID => 'The base campaign ID',
|
||||
self::BID_MODIFIER_VALUE => 'The bid modifier value',
|
||||
self::BILLING_SETUP_ID => 'The billing setup ID',
|
||||
self::BRIDGE_MAP_VERSION_ID
|
||||
=> 'The version of partner IDs to be used for store-sale uploads',
|
||||
self::BUSINESS_ACCOUNT_IDENTIFIER => 'The account number of the Business Profile account',
|
||||
self::BUSINESS_PROFILE_LOCATION => 'The Business Profile location resource name',
|
||||
self::BUSINESS_NAME => 'The Business Profile business name',
|
||||
self::CALL_START_DATE_TIME => 'The call start date time',
|
||||
self::CALLER_ID => 'The caller ID',
|
||||
self::CALLOUT_TEXT => 'The callout text',
|
||||
self::CAMPAIGN_BUDGET_ID => 'The campaign budget ID',
|
||||
self::CAMPAIGN_EXPERIMENT_ID => 'The campaign experiment ID',
|
||||
self::CAMPAIGN_ID => 'The campaign ID',
|
||||
self::CAMPAIGN_IDS => 'The campaign IDs',
|
||||
self::CARRIER_COUNTRY_CODE => 'The carrier country code',
|
||||
self::CHAIN_ID => 'The retail chain ID',
|
||||
self::CHECK_IN_DAY_CRITERION_ID => 'The hotel check-in day criterion ID',
|
||||
self::CONVERSION_ACTION_ID => 'The conversion action ID',
|
||||
self::CONVERSION_ACTION_IDS => 'The conversion action IDs',
|
||||
self::CONVERSION_CUSTOM_VARIABLE_ID => 'The conversion custom variable ID',
|
||||
self::CONVERSION_CUSTOM_VARIABLE_VALUE => 'The conversion custom variable value',
|
||||
self::CONVERSION_DATE_TIME => 'The conversion date time',
|
||||
self::CONVERSION_RATE_MODIFIER => 'The conversion rate modifier',
|
||||
self::CONVERSION_VALUE => 'The conversion value',
|
||||
self::COUNTRY_CODE => 'The country code',
|
||||
self::CPC_BID_CEILING_MICRO_AMOUNT => 'The CPC bid ceiling micro amount',
|
||||
self::CPC_BID_MICRO_AMOUNT => 'The CPC bid micro amount',
|
||||
self::CRITERION_ID => 'The criterion ID',
|
||||
self::CURRENCY_CODE => 'The currency code',
|
||||
self::CUSTOMER_ID => 'The customer ID without dashes',
|
||||
self::CUSTOMIZER_ATTRIBUTE_NAME => 'The customizer attribute name',
|
||||
self::CUSTOM_KEY => 'The custom key',
|
||||
self::DRAFT_ID => 'The draft ID',
|
||||
self::EMAIL_ADDRESS => 'The email address',
|
||||
self::END_DATE_TIME => 'The end date time',
|
||||
self::EXTERNAL_ID => 'The external ID',
|
||||
self::FEED_ID => 'The feed ID',
|
||||
self::FEED_ITEM_ID => 'The feed item ID',
|
||||
self::FEED_ITEM_IDS => 'The feed item IDs',
|
||||
self::FEED_ITEM_SET_ID => 'The feed item set ID',
|
||||
self::FINAL_URL => 'The final URL',
|
||||
self::FLIGHT_PLACEHOLDER_FIELD_NAME => 'The flight placeholder field name',
|
||||
self::FREE_FORM_KEYWORD_TEXT => 'The free-form keyword text',
|
||||
self::GBRAID => 'The GBRAID identifier for an iOS app conversion',
|
||||
self::GCLID => 'The Google Click ID',
|
||||
self::GEO_TARGET_CONSTANT_ID => 'The geo target constant ID',
|
||||
self::BUSINESS_PROFILE_ACCESS_TOKEN => 'The access token used for uploading Business Profile '
|
||||
. 'location feed data',
|
||||
self::BUSINESS_PROFILE_EMAIL => 'The email address associated with the Business Profile account',
|
||||
self::HOTEL_CENTER_ACCOUNT_ID => 'The hotel center account ID',
|
||||
self::IMAGE_ASSET_ID => 'The image asset ID',
|
||||
self::ITEM_ID => 'The item ID',
|
||||
self::KEYWORD_PLAN_ID => 'The keyword plan ID',
|
||||
self::KEYWORD_TEXT => 'The keyword text',
|
||||
self::KEYWORD_TEXTS => 'The list of keyword texts',
|
||||
self::LABEL_ID => 'The label ID',
|
||||
self::LANGUAGE_CODE => 'The language code',
|
||||
self::LANGUAGE_ID => 'The language ID',
|
||||
self::LANGUAGE_NAME => 'The language name',
|
||||
self::LOCALE => 'The locale',
|
||||
self::LOCATION_ID => 'The location ID',
|
||||
self::LOCATION_IDS => 'The list of location IDs',
|
||||
self::LOCATION_NAMES => 'The list of location names',
|
||||
self::LOGIN_CUSTOMER_ID => 'The login customer ID',
|
||||
self::MANAGER_CUSTOMER_ID => 'The manager customer ID',
|
||||
self::MARKETING_IMAGE_ASSET_ID => 'The ID of marketing image asset',
|
||||
self::MERCHANT_CENTER_ACCOUNT_ID => 'The Merchant center account ID',
|
||||
self::NAME_PREFIX => 'The name prefix',
|
||||
self::OFFLINE_USER_DATA_JOB_ID => 'The offline user data job ID',
|
||||
self::OFFLINE_USER_DATA_JOB_TYPE => 'The offline user data job type',
|
||||
self::OMIT_UNSELECTED_RESOURCE_NAMES => 'Whether to omit unselected resource names',
|
||||
self::ORDER_ID => 'The order ID',
|
||||
self::OUTPUT_FILE_PATH => 'The output file path',
|
||||
self::PAGE_URL => 'The page URL',
|
||||
self::PARTNER_ID => 'The partner ID',
|
||||
self::PAYMENTS_ACCOUNT_ID => 'The payments account ID',
|
||||
self::PAYMENTS_PROFILE_ID => 'The payments profile ID',
|
||||
self::PERCENT_CPC_BID_MICRO_AMOUNT =>
|
||||
'The CPC bid micro amount for the Percent CPC bidding strategy',
|
||||
self::PHONE_COUNTRY => 'The phone country',
|
||||
self::PHONE_NUMBER => 'The phone number',
|
||||
self::PLACE_ID => 'The place ID',
|
||||
self::RECOMMENDATION_ID => 'The recommendation ID',
|
||||
self::RESTATEMENT_VALUE => 'The restatement value',
|
||||
self::CREATE_DEFAULT_LISTING_GROUP =>
|
||||
'Whether it should create a default listing group',
|
||||
self::DELETE_EXISTING_FEEDS =>
|
||||
'Whether it should delete the existing feeds',
|
||||
self::REPLACE_EXISTING_TREE =>
|
||||
'Whether it should replace the existing listing group tree on an ad group/asset group',
|
||||
self::QUANTITY => 'The quantity',
|
||||
self::RUN_JOB => 'Whether it should run the offline user data job',
|
||||
self::SALES_COUNTRY => 'The sales country',
|
||||
self::SITELINK_TEXT => 'The sitelink text',
|
||||
self::SQUARE_MARKETING_IMAGE_ASSET_ID => 'The ID of square marketing image asset',
|
||||
self::START_DATE_TIME => 'The start date time',
|
||||
self::USER_AGENT => 'The user agent',
|
||||
self::USER_LIST_ID => 'The user list ID',
|
||||
self::USER_LIST_IDS => 'The user list IDs',
|
||||
self::THINGS_TO_DO_CENTER_ACCOUNT_ID => 'The Things to Do Center account ID',
|
||||
self::WBRAID => 'The WBRAID identifer for an iOS web conversion'
|
||||
];
|
||||
}
|
110
app/util/ArgumentParser.php
Normal file
110
app/util/ArgumentParser.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use GetOpt\ArgumentException;
|
||||
use GetOpt\GetOpt;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Wraps `GetOpt` and normalizes arguments parsed by it.
|
||||
*
|
||||
* @see GetOpt
|
||||
*/
|
||||
class ArgumentParser
|
||||
{
|
||||
/**
|
||||
* Parses any arguments specified via the command line. For those in the provided argument
|
||||
* names that are not passed, provides null values instead.
|
||||
*
|
||||
* @param array $argumentNames the associative array of argument names to their types
|
||||
* @return array the argument names to their values
|
||||
*/
|
||||
public function parseCommandArguments(array $argumentNames)
|
||||
{
|
||||
$getOpt = new GetOpt();
|
||||
$normalizedOptions = [];
|
||||
$numRequiredArguments = 0;
|
||||
$getOpt->addOption(['h', 'help', GetOpt::NO_ARGUMENT, 'Show this help and quit']);
|
||||
foreach ($argumentNames as $argumentName => $argumentType) {
|
||||
$normalizedOptions[$argumentName] = null;
|
||||
// Adds an option for an argument using a long option name only.
|
||||
$getOpt->addOption(
|
||||
[
|
||||
null,
|
||||
$argumentName,
|
||||
$argumentType,
|
||||
ArgumentNames::$ARGUMENTS_TO_DESCRIPTIONS[$argumentName]
|
||||
]
|
||||
);
|
||||
|
||||
if ($argumentType === GetOpt::REQUIRED_ARGUMENT) {
|
||||
$numRequiredArguments++;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse arguments and catch exceptions.
|
||||
try {
|
||||
$getOpt->process();
|
||||
} catch (ArgumentException $exception) {
|
||||
// When there are any errors regarding arguments, such as invalid argument names, or
|
||||
// specifying required arguments but not providing values, 'ArgumentException' will
|
||||
// be thrown. Show the help text in these cases.
|
||||
echo PHP_EOL . $getOpt->getHelpText();
|
||||
throw $exception;
|
||||
}
|
||||
// Show help text when requested.
|
||||
if (!is_null($getOpt->getOption('help'))) {
|
||||
$this->printHelpMessageAndExit($getOpt);
|
||||
// Help text is printed, so no arguments are passed. The below line is reached only
|
||||
// in tests.
|
||||
return [];
|
||||
}
|
||||
|
||||
$numPassedRequiredArguments = 0;
|
||||
foreach ($getOpt->getOptions() as $optionName => $optionValue) {
|
||||
if ($argumentNames[$optionName] === GetOpt::REQUIRED_ARGUMENT) {
|
||||
$numPassedRequiredArguments++;
|
||||
}
|
||||
$normalizedOptions[$optionName] = $optionValue;
|
||||
}
|
||||
// Don't allow the case when optional arguments are passed, but required arguments are not.
|
||||
if (
|
||||
count($getOpt->getOptions()) > 0
|
||||
&& $numPassedRequiredArguments !== $numRequiredArguments
|
||||
) {
|
||||
echo PHP_EOL . $getOpt->getHelpText();
|
||||
throw new InvalidArgumentException(
|
||||
'All required arguments must be specified.' . PHP_EOL
|
||||
);
|
||||
}
|
||||
return $normalizedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the help message and exit the program.
|
||||
*
|
||||
* @param GetOpt $getOpt the GetOpt object to print its help text
|
||||
*/
|
||||
public function printHelpMessageAndExit(GetOpt $getOpt)
|
||||
{
|
||||
echo PHP_EOL . $getOpt->getHelpText();
|
||||
exit;
|
||||
}
|
||||
}
|
211
app/util/Feeds.php
Normal file
211
app/util/Feeds.php
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use Google\Ads\GoogleAds\Lib\V18\GoogleAdsClient;
|
||||
use Google\Ads\GoogleAds\V18\Enums\FlightPlaceholderFieldEnum\FlightPlaceholderField;
|
||||
use Google\Ads\GoogleAds\V18\Enums\RealEstatePlaceholderFieldEnum\RealEstatePlaceholderField;
|
||||
use Google\Ads\GoogleAds\V18\Resources\FeedAttribute;
|
||||
use Google\Ads\GoogleAds\V18\Resources\FeedItem;
|
||||
use Google\Ads\GoogleAds\V18\Resources\FeedItemAttributeValue;
|
||||
use Google\Ads\GoogleAds\V18\Services\GoogleAdsRow;
|
||||
use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsRequest;
|
||||
|
||||
/**
|
||||
* Utilities that are shared between code examples related to feeds.
|
||||
*/
|
||||
final class Feeds
|
||||
{
|
||||
/**
|
||||
* Retrieves a feed item and its attribute values given a resource name.
|
||||
*
|
||||
* @param string $feedItemResourceName the feed item resource name
|
||||
* @param int $customerId the customer ID
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @return FeedItem the feed item
|
||||
*/
|
||||
public static function feedItemFor(
|
||||
string $feedItemResourceName,
|
||||
int $customerId,
|
||||
GoogleAdsClient $googleAdsClient
|
||||
) {
|
||||
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||
// Constructs the query to get the feed item with attribute values.
|
||||
$query = "SELECT feed_item.attribute_values FROM feed_item"
|
||||
. " WHERE feed_item.resource_name = '$feedItemResourceName'";
|
||||
// Issues a search request.
|
||||
$response =
|
||||
$googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));
|
||||
|
||||
// Returns the feed item attribute values, which belongs to the first item. We can ensure
|
||||
// it belongs to the first one because we specified the feed item resource name in the
|
||||
// query.
|
||||
return $response->getIterator()->current()->getFeedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the target feed item attribute value. This is needed to specify which feed
|
||||
* item attribute value will be updated in the given feed item.
|
||||
*
|
||||
* @param FeedItemAttributeValue $targetFeedItemAttributeValue the new feed item attribute value
|
||||
* that will be updated
|
||||
* @param FeedItem $feedItem the feed item that will be updated. It should be populated with
|
||||
* the current attribute values
|
||||
* @return int the attribute index
|
||||
*/
|
||||
public static function attributeIndexFor(
|
||||
FeedItemAttributeValue $targetFeedItemAttributeValue,
|
||||
FeedItem $feedItem
|
||||
) {
|
||||
$attributeIndex = -1;
|
||||
// Loops through attribute values to find the index of the feed item attribute value to
|
||||
// update.
|
||||
foreach ($feedItem->getAttributeValues() as $feedItemAttributeValue) {
|
||||
/** @var FeedItemAttributeValue $feedItemAttributeValue */
|
||||
$attributeIndex++;
|
||||
// Checks if the current feedItemAttributeValue is the one we are updating
|
||||
if (
|
||||
$feedItemAttributeValue->getFeedAttributeId()
|
||||
=== $targetFeedItemAttributeValue->getFeedAttributeId()
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($attributeIndex === -1) {
|
||||
throw new \InvalidArgumentException(
|
||||
'No matching feed attribute for feed item attribute ID: '
|
||||
. $targetFeedItemAttributeValue->getFeedAttributeId()
|
||||
);
|
||||
}
|
||||
|
||||
return $attributeIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the place holder fields to feed attributes map for a flights feed.
|
||||
* See FlightPlaceholderField.php for all available placeholder field values.
|
||||
*
|
||||
* @see Feeds::placeholderFieldsMapFor()
|
||||
*
|
||||
* @param string $feedResourceName the feed resource name to get the attributes from
|
||||
* @param int $customerId the customer ID
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @return array the map from placeholder fields to feed attributes
|
||||
*/
|
||||
public static function flightPlaceholderFieldsMapFor(
|
||||
string $feedResourceName,
|
||||
int $customerId,
|
||||
GoogleAdsClient $googleAdsClient
|
||||
) {
|
||||
return self::placeholderFieldsMapFor(
|
||||
$feedResourceName,
|
||||
$customerId,
|
||||
$googleAdsClient,
|
||||
[
|
||||
'Flight Description' => FlightPlaceholderField::FLIGHT_DESCRIPTION,
|
||||
'Destination ID' => FlightPlaceholderField::DESTINATION_ID,
|
||||
'Flight Price' => FlightPlaceholderField::FLIGHT_PRICE,
|
||||
'Flight Sale Price' => FlightPlaceholderField::FLIGHT_SALE_PRICE,
|
||||
'Final URLs' => FlightPlaceholderField::FINAL_URLS
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the place holder fields to feed attributes map for a real estate feed.
|
||||
* See RealEstatePlaceholderField.php for all available placeholder field values.
|
||||
*
|
||||
* @see Feeds::placeholderFieldsMapFor()
|
||||
*
|
||||
* @param string $feedResourceName the feed resource name to get the attributes from
|
||||
* @param int $customerId the customer ID
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @return array the map from placeholder fields to feed attributes
|
||||
*/
|
||||
// [START add_real_estate_feed]
|
||||
public static function realEstatePlaceholderFieldsMapFor(
|
||||
string $feedResourceName,
|
||||
int $customerId,
|
||||
GoogleAdsClient $googleAdsClient
|
||||
) {
|
||||
return self::placeholderFieldsMapFor(
|
||||
$feedResourceName,
|
||||
$customerId,
|
||||
$googleAdsClient,
|
||||
[
|
||||
'Listing ID' => RealEstatePlaceholderField::LISTING_ID,
|
||||
'Listing Name' => RealEstatePlaceholderField::LISTING_NAME,
|
||||
'Final URLs' => RealEstatePlaceholderField::FINAL_URLS,
|
||||
'Image URL' => RealEstatePlaceholderField::IMAGE_URL,
|
||||
'Contextual Keywords' => RealEstatePlaceholderField::CONTEXTUAL_KEYWORDS
|
||||
]
|
||||
);
|
||||
}
|
||||
// [END add_real_estate_feed]
|
||||
|
||||
/**
|
||||
* Retrieves the placeholder fields to feed attributes map for a feed. The initial
|
||||
* query retrieves the feed attributes, or columns, of the feed. Each feed attribute will also
|
||||
* include the feed attribute ID, which will be used in a subsequent step.
|
||||
*
|
||||
* Then a map is created for the feed attributes (columns) and returned:
|
||||
* - The keys are the placeholder types that the columns will be.
|
||||
* - The values are the feed attributes.
|
||||
*
|
||||
* @param string $feedResourceName the feed resource name to get the attributes from
|
||||
* @param int $customerId the customer ID
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param array $feedAttributeNamesMap the associative array mapping from feed attribute names
|
||||
* to placeholder fields
|
||||
* @return array the map from placeholder fields to feed attributes
|
||||
*/
|
||||
private static function placeholderFieldsMapFor(
|
||||
string $feedResourceName,
|
||||
int $customerId,
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
array $feedAttributeNamesMap
|
||||
) {
|
||||
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||
// Constructs the query to get the feed attributes for the specified feed resource name.
|
||||
$query = "SELECT feed.attributes FROM feed WHERE feed.resource_name = '$feedResourceName'";
|
||||
// Issues a search request.
|
||||
$response =
|
||||
$googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));
|
||||
|
||||
// Gets the first result because we only need the single feed we created previously.
|
||||
/** @var GoogleAdsRow $googleAdsRow */
|
||||
$googleAdsRow = $response->getIterator()->current();
|
||||
|
||||
// Gets the attributes list from the feed and creates a map with keys of placeholder fields
|
||||
// and values of feed attributes.
|
||||
$feedAttributes =
|
||||
iterator_to_array($googleAdsRow->getFeed()->getAttributes()->getIterator());
|
||||
$placeholderFields = array_map(
|
||||
function (FeedAttribute $feedAttribute) use ($feedAttributeNamesMap) {
|
||||
if (!array_key_exists($feedAttribute->getName(), $feedAttributeNamesMap)) {
|
||||
throw new \RuntimeException('Invalid feed attribute name.');
|
||||
}
|
||||
return $feedAttributeNamesMap[$feedAttribute->getName()];
|
||||
},
|
||||
$feedAttributes
|
||||
);
|
||||
return array_combine($placeholderFields, $feedAttributes);
|
||||
}
|
||||
}
|
67
app/util/Helper.php
Normal file
67
app/util/Helper.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* General utilities that are shared between code examples.
|
||||
*/
|
||||
final class Helper
|
||||
{
|
||||
/**
|
||||
* Generates a printable string for the current date and time in local time zone.
|
||||
* @return string the result string
|
||||
*/
|
||||
public static function getPrintableDatetime(): string
|
||||
{
|
||||
return (new DateTime())->format("Y-m-d\TH:i:s.vP");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a short printable string for the current date and time in local time zone.
|
||||
* @return string the result string
|
||||
*/
|
||||
public static function getShortPrintableDatetime(): string
|
||||
{
|
||||
return (new DateTime())->format("mdHisv");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an amount from the micro unit to the base unit.
|
||||
*
|
||||
* @param int|float|null $amount the amount in micro unit
|
||||
* @return float the amount converted to the base unit if not null otherwise 0
|
||||
*/
|
||||
public static function microToBase($amount): float
|
||||
{
|
||||
return $amount ? $amount / 1000000.0 : 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an amount from the base unit to the micro unit.
|
||||
*
|
||||
* @param float|int|null $amount the amount in base unit
|
||||
* @return int the amount converted to the micro unit if not null otherwise 0
|
||||
*/
|
||||
public static function baseToMicro($amount): int
|
||||
{
|
||||
return $amount ? (int) ($amount * 1000000) : 0;
|
||||
}
|
||||
}
|
5
config/plugin/webman/auto-route/app.php
Normal file
5
config/plugin/webman/auto-route/app.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
return [
|
||||
'enable' => false,
|
||||
'default_app' => '', //默认应用,如需开启请填写默认应用的名称
|
||||
];
|
109
config/plugin/webman/auto-route/route.php
Normal file
109
config/plugin/webman/auto-route/route.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Webman\Route;
|
||||
|
||||
// 已经设置过路由的uri则忽略
|
||||
$routes = Route::getRoutes();
|
||||
$ignore_list = [];
|
||||
foreach ($routes as $tmp_route) {
|
||||
$ignore_list[$tmp_route->getPath()] = 0;
|
||||
}
|
||||
|
||||
$default_app = config('plugin.webman.auto-route.app.default_app');
|
||||
|
||||
$suffix = config('app.controller_suffix', '');
|
||||
$suffix_length = strlen($suffix);
|
||||
|
||||
// 递归遍历目录查找控制器自动设置路由
|
||||
$dir_iterator = new \RecursiveDirectoryIterator(app_path());
|
||||
$iterator = new \RecursiveIteratorIterator($dir_iterator);
|
||||
foreach ($iterator as $file) {
|
||||
// 忽略目录和非php文件
|
||||
if (is_dir($file) || $file->getExtension() != 'php') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_path = str_replace('\\', '/',$file->getPathname());
|
||||
// 文件路径里不带controller的文件忽略
|
||||
if (strpos(strtolower($file_path), '/controller/') === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 只处理带 controller_suffix 后缀的
|
||||
if ($suffix_length && substr($file->getBaseName('.php'), -$suffix_length) !== $suffix) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据文件路径计算uri
|
||||
$uri_path = str_replace(['/controller/', '/Controller/'], '/', substr(substr($file_path, strlen(app_path())), 0, - (4 + $suffix_length)));
|
||||
|
||||
// 默认应用
|
||||
$is_default_app = false;
|
||||
if (is_string($default_app) && !empty($default_app)) {
|
||||
$seg = explode('/', $uri_path);
|
||||
if ($seg[1] == $default_app) {
|
||||
$uri_path = str_replace($default_app . '/', '', $uri_path);
|
||||
$is_default_app = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据文件路径是被类名
|
||||
$class_name = str_replace('/', '\\',substr(substr($file_path, strlen(base_path())), 0, -4));
|
||||
|
||||
if (!class_exists($class_name)) {
|
||||
echo "Class $class_name not found, skip route for it\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
// 通过反射找到这个类的所有共有方法作为action
|
||||
$class = new ReflectionClass($class_name);
|
||||
$class_name = $class->name;
|
||||
$methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
|
||||
$route = function ($uri, $cb) use ($ignore_list) {
|
||||
if (isset($ignore_list[strtolower($uri)])) {
|
||||
return;
|
||||
}
|
||||
Route::any($uri, $cb);
|
||||
if ($uri !== '') {
|
||||
Route::any($uri . '/', $cb);
|
||||
}
|
||||
$lower_uri = strtolower($uri);
|
||||
if ($lower_uri !== $uri) {
|
||||
Route::any($lower_uri, $cb);
|
||||
Route::any($lower_uri . '/', $cb);
|
||||
}
|
||||
};
|
||||
|
||||
// 设置路由
|
||||
foreach ($methods as $item) {
|
||||
$action = $item->name;
|
||||
if (in_array($action, ['__construct', '__destruct'])) {
|
||||
continue;
|
||||
}
|
||||
// action为index时uri里末尾/index可以省略
|
||||
if ($action === 'index') {
|
||||
// controller也为index时uri里可以省略/index/index
|
||||
if (strtolower(substr($uri_path, -6)) === '/index') {
|
||||
if ($is_default_app) {
|
||||
$route('/', [$class_name, $action]);
|
||||
}
|
||||
$route(substr($uri_path, 0, -6), [$class_name, $action]);
|
||||
}
|
||||
$route($uri_path, [$class_name, $action]);
|
||||
}
|
||||
$route($uri_path.'/'.$action, [$class_name, $action]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user