I'm working with Google Adwords API in Laravel but I'm pretty new and I need help to understand one thing on a line of code where I got the question I used for this post.
The line is this:
$campaign_ids = array_filter(Arr::pluck($campaigns, 'googleId'));
Being part of this function:
public function reports(CampaignGroup $campaignGroup, string $dateFrom, string $dateTo, int $limit): array
{
$campaigns = [];
$campaigns = array_merge($campaigns, $campaignGroup->campaigns->toArray());
$campaign_ids = array_filter(Arr::pluck($campaigns, 'googleId'));
$cacheKey = sprintf('keyword_performance_report.%d.%d', $this->account->id, $limit);
$cacheTtl = DateInterval::createFromDateString($this->cacheTtl);
$definitionAdapter = new KeywordsPerformanceDefinitionAdapter($campaign_ids);
return Cache::remember($cacheKey, $cacheTtl, function () use ($definitionAdapter, $dateFrom, $dateTo, $campaign_ids, $limit) {
$definitionAdapter->applyFilters($campaign_ids, $dateFrom, $dateTo, $limit);
$definitionAdapter->getSelectorAdapter()->asSummary();
$response = $this->generate();
return $response['entries'];
});
}
I would like to understand what happening if that line will be come an empty array and Google will get that. I was unable to get that and I would like to understand what kind of exception can be throw here and what should I do to avoid such an issue.
In general, if you pass an empty $campaign_ids array, I would expect that statistics for all keywords would be returned, as no filtering by campaign is requested.
However, seeing as KeywordsPerformanceDefinitionAdapter looks to be some custom code of your application, it might obviously be possible that the actual behavior is different.
Can't you just test the scenario and take note of any error conditions encountered?
Related
In my code I am trying to get access my data bank through query builder, everything works I am able to get access to everything as a string (which this is what I want), everything but my date is returned as a class DateTime object in my Frontend, the intersting part for me through Api I am getting my date as a string and not as a class DateTime object and I cant understand why I am getting two different results when I am using the same methods same query builder and my Question is why am I getting two different results? is it possible to get different result through Api? and if so why? and is there a way to convert the class DateTime object to a string?
Api Controller
public function indexAction()
{
$request = $this->Request();
$limit = $request->getParam('limit', 1000);
$offset = $request->getParam('start', 0);
$sort = $request->getParam('sort', []);
$filter = $request->getParam('filter', []);
$result = $this->resource->getList($offset, $limit, $filter, $sort);
$view = $this->View();
$view->assign($result);
$view->assign('success', true);
}
Fronend Controller
public function listAction()
{
$feedback= $this->resource->getList(0, 10, null, null);
$this->View()->assign('feedback', $feedback);
}
QueryBuilder
protected function getBaseQuery()
{
$builder = $this->getManager()->createQueryBuilder();
$builder->select(['feedback', 'user_id.firstname','user_id.lastname',])
->from(FeedbackModel::class, 'feedback')
->leftJoin('feedback.customer', 'user_id');
return $builder;
}
getList function
public function getList($offset, $limit, $filter, $sort)
{
$this->checkPrivilege('read');
$builder = $this->getBaseQuery();
$builder->setFirstResult($offset)
->setMaxResults($limit);
if (!empty($filter)){
$builder->addFilter($filter);
}
if (!empty($sort)){
$builder->addOrderBy($sort);
}
$query = $builder->getQuery();
$query->setHydrationMode($this->getResultMode());
$paginator = $this->getManager()->createPaginator($query);
$totalResult = $paginator->count();
$feedback = $paginator->getIterator()->getArrayCopy();
return ['data' => $feedback , 'total' => $totalResult];
}
Api result
data
0
0
id 1
feedback "this shop is boring"
date "2022-12-07T00:00:00+0100"
public true
firstname "some"
lastname "thing"
total 1
success true
Frontend Result
0 => Array (3)
0 => Array (4)
id => 1
feedback => "this shop is boring"
date => DateTime Object (0)
public => true
firstname => "some"
lastname => "thing"
total => 1
->nocache = null
How I fixed my Problem and what it was:
With the help of #ADyson I found out what the problem was. I was tackling the problem from the wrong way though I didn't need to convert my DateTime to string neither in my Model, in my Controller or my Query-builder the only problem was the way I was calling it, there is a way of calling date objects like this in smarty and its by using |date:'dd.MM.y' in my case it was {$feedbacks.date|date:'dd.MM.y'} this get the date inside of the class and converts it to a string at the same time and like that I got to call the date that I want.
To answer my Original Question:
Api and Frontend don't give different result but a different Format which is for the human eye a bit different with the use of different Tools such as PHPStorm Debugger, Postman and Smarty Debugger I got to see the different result, why I am getting these Kind of results and the data inside of the class DateTime in my case.
what helped me find a solution my to problem
PHPStorm Debugger.
Smarty Debugger
Postman
I have this controller for a RESTful API I am building in Laravel Lumen which takes a relatively big amount of parameters and parses them into where queries, and data is fetched depending on if they were provided. For example,
GET /nodes?region=California
GET /nodes?ip=127.0.0.1
I am currently taking them in the constructor, building an array of the parameters (since I couldn't figure out how to get the raw get array in Lumen and it would be inconvenient because I already have other parameters there), and filtering out the null values (I am setting values to null if they are not in the query).
Now, when it comes to filtering the values each in the array, I am doing it by a foreach array. This is the cleanest way I could figure out to do it, without too much code (I don't want to make my controllers too fat.).
Is there any other way to do this cleanly, maybe with separation of functions/classes?
Here is my constructor code:
/**
* Get some values before using functions.
*
* #param Request $request Instance of request.
*/
public function __construct(Request $request)
{
$this->offset = (int) $request->input('offset', 0);
// TODO: I'm not sure how to implement this, code in question
$this->filters = [
'region' => $request->input('region', null),
'name' => $request->input('name', null),
'ip' => $request->input('ip', null)
];
$this->filters = array_filter($this->filters, function ($v) {
return !is_null($v);
});
// Set a sane SQL limit.
$this->limit = 5;
$this->request = $request;
}
And the controller code:
/**
* List all nodes.
*
* #return [string] [JSON containing list of nodes, if sorted.]
*/
public function all()
{
try {
// use filters provided
$data = Nodes::limit($this->limit)->offset($this->offset);
foreach ($this->filters as $filter => $value) {
$data->where($filter, $value);
}
$data = $data->get();
$response = $this->respond($data);
} catch (\Exception $e) {
$response = $this->respondServerError('Could not retrieve data from database.');
}
return $response;
}
So any time I have to do filtering of a resource-list in an API, here's how I do it.
First off though, before I begin, a quick tip concerning getting the Request object when you're in your controller method: If you add Request $request as a parameter for your all() function, you will have access to the $request variable there, same as your constructor. So the complete signature would be public function all(Request $request). Controller methods have the same magic dependency injection that other class constructors get in Laravel/Lumen. Alternatively, in your function you can always ask the app() function to give you an object of a specific class. Because the Request object is bound in the Container to just 'request', you can ask for the full class name, or just 'request': $request = app('request');
So once I have my request object, inside my controller method I like to go through each filter either as a group, or one-by-one, depending on how complex each filter is. Sometimes filters are complex, like a list of comma-separated IDs that need to be exploded into an array. If it's just simple string filters though, I tend to throw the list into an array and run through that.
Here's an example function to illustrate some ideas:
public function getIndex(Request $request)
{
//Create a User object to append WHERE clauses onto
$user = app('App\Models\User');
//Run through our simple text fields
foreach(['first_name', 'last_name', 'region', 'ip'] as $field) {
if ($request->has($field)) {
$user->where($field, $request->input($field));
}
}
//This field uses a LIKE match, handle it separately
if ($request->has('email')) {
$user->where('email', LIKE, '%' . $request->input('email') . '%');
}
//This field is a list of IDs
if ($request->has('id')) {
$ids = explode(',', $request->input('id'));
$user->whereIn('id', $ids);
}
//Use pagination
$users = $user->paginate(25);
/**
* Continue with the rest of response formatting below here
*/
}
You'll notice I used the paginate function to limit my results. When building an API endpoint that lists resources, you're going to want to put in your headers (my preference) or the response body information on how to get the first, previous, next, and last page of results. The Pagination feature in Laravel makes that easy, as it can construct most of the links using the links() method.
Unfortunately, you need to tell it what filter parameters were passed in the request so it can make sure it adds those to the links it generates. Otherwise you'll get links back without your filters, which doesn't do the client very much good for paging.
So here's a more complete example of recording filter parameters so they can be appended onto pagination links:
public function getIndex(Request $request)
{
//Create a User object to append WHERE clauses onto
$user = app('App\Models\User');
//List of filters we found to append to links later
$appends = [];
//Run through our simple text fields
foreach(['first_name', 'last_name', 'region', 'ip'] as $field) {
if ($request->has($field)) {
$appends[$field] = $request->input($field);
$user->where($field, $request->input($field));
}
}
//This field uses a LIKE match, handle it separately
if ($request->has('email')) {
$appends['email'] = $request->input('email');
$user->where('email', LIKE, '%' . $request->input('email') . '%');
}
//This field is a list of IDs
if ($request->has('id')) {
$appends['id'] = $request->input('id');
$ids = explode(',', $request->input('id'));
$user->whereIn('id', $ids);
}
//Use pagination
$users = $user->paginate(25);
//Make sure we append our filter parameters onto the pagination object
$users->appends($appends);
//Now calling $users->links() will return the correct links with the right filter info
/**
* Continue with the rest of response formatting below here
*/
}
Pagination documentation can be found here: https://laravel.com/docs/5.2/pagination
For an example of how pagination linking can be awesomely done, check out Github's API documentation: https://developer.github.com/v3/#pagination
In the end it's not too far off from what you were doing, conceptually. The advantage here is that you move the code into the method that needs it, instead of having it run in your constructor every single time the controller is initialized, even if a different method will be called.
Hope that helps!
The Context
I'm using Laravel's Eloquent as my ORM. I am creating an API endpoint which provides access to Cars which have several attributes (color, make, status).
My endpoint allows clients to filter the return value by any subset of those attributes, if they provide no attributes then I will return everything.
The Question
I want to build a conditional query, which starts from "all" and narrows down based on which parameters have been specified. Here's what I've written:
public function getCars(Request $request)
{
$results = Cars::all();
if($request->has('color'))
$results = $results->where('color', $request->input('color'));
if($request->has('make'))
$results = $results->where('make', $request->input('make'));
if($request->has('status'))
$results = $results->where('status', $request->input('status'));
return $results->toJson();
}
If I call this with no parameters the API returns a list of all cars in the database.
If, however, I specify (for instance) status of 0 the API returns an empty set, despite the fact that some cars have status of 0.
Am I approaching this incorrectly? Is there something fundamental I'm missing?
Note that if instead I write:
$results = Cars::where('status', 0);
return $results->get();
The list of cars is properly generated
You should change your function like this:
public function getCars(Request $request)
{
$results = Cars::query();
if($request->has('color'))
$results = $results->where('color', $request->input('color'));
if($request->has('make'))
$results = $results->where('make', $request->input('make'));
if($request->has('status'))
$results = $results->where('status', $request->input('status'));
return $results->get()->toJson();
}
You could try this, for simplicity.
$query = Cars::query(); // no query executed, just give us a builder
$query->where(array_only($request->all(), ['color', 'make', 'status'])); // where can take a key value array to use
// update: only taking the vars you need, never trust incoming data
return $query->get(); // will be converted to Json for you
This only queries the DB for what you need. Yours is returning all results then filtering through them in a collection.
Update:
As Joseph stated, there is different functionality between $request->only() and array_only. The functionality of array_only is wanted here.
I'm currently coding a newsletter system. In order to send the mail, I need to get all e-mail addresses from my database (of course).
So I created a custom repository method, as follows :
public function getEmailAddresses()
{
$query = $this->getEntityManager()->createQueryBuilder()
->select('u.email')
->from('AppBundle:User', 'u')
->where('u.isNewsletterSubscriber = true')
;
$results = $query->getQuery()->getResult();
$addresses = [];
foreach($results as $line) {
$addresses[] = $line['email'];
}
return $addresses;
}
I am wondering if there is a better way to do so than treating the result to get a "plain" array containing only e-mail addresses. In effect, after $query->getQuery()->getResult(), I get something like this :
'results' =>
[0] => array('email' => 'first#email.com')
[1] => array('email' => 'second#email.com')
And as I said, I want something like this :
array('first#email.com', 'second#email.com')
Does a cleaner way to do that exist using Doctrine2 built-in methods ? I've tried with different hydratation modes but nothing worked.
Thanks in advance :)
You could probably create a custom hydrator, but there's really no issue with just doing it the way you are right now. You could also do it in the following ways:
PHP <= 5.4
return array_map('current', $addresses);
PHP >= 5.5
return array_column($addresses, 'email');
The array_column function was introduced in PHP 5.5.0 and does what you're looking for. The array_map function will work otherwise, calling PHP's internal current function which simply returns the value of the current element (which is always initialized to the first element of that array).
Be careful with using array_map if you have a large number of rows returned, because it will likely be slower and it will definitely take up a lot more memory since it has to copy the array.
You can run pure sql with doctrine (DBAL):
example:
public function getEmails()
{
$connection = $this->getEntityManager()->getConnection()->prepare('SELECT u.email FROM user AS u');
$connection->execute();
return $connection->fetchAll(\PDO::FETCH_COLUMN);
}
Try other $hydrationModes, maybe that help
getResult( mixed $hydrationMode = Doctrine\ORM\AbstractQuery::HYDRATE_OBJECT )
I would rather use the getArrayResult method, so doctrine must not hydrate each object (this is the expensive task from doctrine).
public function getEmailAddresses()
{
$q = $this->getEntityManager()->createQuery('SELECT u.email FROM AppBundle:User u WHERE u.isNewsletterSubscriber = true');
return array_map('current', $q->getArrayResult());
}
I am writing a method which can call any method from any class (this process is dynamic).
In my method, I need to find out what type is the returned value, based on the returned value type,I will proceed on to the next step.
For example:
<?php
function identifyReturnType($className, $methodName) {
$result = $className->$methodName();
//Here I need to find out the $result type
}
?>
I have many classes where methods return bool, string, int etc.
and there are a few methods which do not return anything, those methods set the values in object or the object has resource pointer :
<?php
function getCategories() {
$this->query("SELECT * FROM categories");
}
function getItems() {
$this->query("SELECT * FROM items");
$this->getValues();
}
?>
PHP gettype($var) method finds out what is the value type but for this, my method must return a value. I have cases (as I explained above) where method just sets the query object.
Please share your ideas.
Thank you so much.
This really depends on your implementation. Some follow architecture where every function will return data as array. Even for query returned data is returned in small chunks of array. That is completely on how you optimize or write your script. Say you are getting all contacts and if you have say 10,000 contacts in DB and you return all in an array, thats a bad idea. Rather use pagination and return in small numbers if you want the function to return data as array.
I have had this issue, where we have a big web application written in PHP/Mysql. Over the time we have thousands of functions across different classes. Now we have to develop a REST API which will have different functionality. The main problem was we do not have used different functions to return query object, some to return array, some to return Boolean and so on. The API should return data as JSON. Now we have to choice use the existing code for different functionality or re-write new code for the API. The 2nd choice is more expensive so we are left with first choice. But the problem as I mentioned is far from over the methods will return different type and do we need to really write more codes to check which function is called and if the say function "xyz()" is called and we know its returning query object then loop through it generate array and then json. No thats a bad idea and will take a lot of effort and its better to write seperate code then.
So we follow the following approach.
Our api call looks like
www.api.oursite.com/api/v1/Resource/Method?param=....
Now we catch the Resource and Method where resource is a Class name and Method is a method name for that Class.
so we know we have to call Resource->Method()
Now we have a class called ResourceMethodMap.class.php and it contains the array as
static $resource_method_map = array(
"Users"=>array(
"getUserInfo"=> // gets the user info
array(
"return"=>"array",
"accessToken"=>true
)
),
....
...
)
So the API request processing code does something like
public function call_method($resource = "",$method=""){
if($resource == "") $resource = $this->get_resource();
if($method == "") $method = $this->get_api_method();
if (class_exists($resource)) {
$resource_obj = new $resource();
// Parse the method params as array
$param_array = $this->parse_method_params($resource,$method);
if(false !== $param_array){
$result = call_user_func_array(array($resource_obj, $method), $param_array);
}else{
$result = $resource_obj->$method() ;
}
return $this->process_return_data($resource,$method,$result,$resource_obj);
}else{
$this->setMessage("Invalid Resource");
return false ;
}
}
Here the function process_return_data() will do the returned data conversion as
function process_return_data($resource,$method,$ret_val,$resource_obj = NULL){
if(array_key_exists("return",ResourceMethodMap::$resource_method_map[$resource][$method])){
$return_type = ResourceMethodMap::$resource_method_map[$resource][$method]["return"];
$return_array= array();
switch($return_type){
case 'boolean':
if(false === $ret_val){
return false ;
}else{
if(is_array($ret_val)){
return $ret_val ;
}elseif(true === $ret_val){
return $ret_val ;
}else{
$return_array[] = $ret_val ;
return $return_array ;
}
}
break;
case 'array':
return $ret_val ;
break;
}
.....
}
}
So Yes it completely on the developer how they want their data to be returned. The above example is just one real time scenario how we have implemented.
I have posted the complete code her http://codepad.org/MPY1gVed have look
If i understood your question right you can do this by passing in an argument as a reference.
Here's an example i made for you, if it is any help.
http://php.net/manual/en/language.references.pass.php
Another solution can be to return an array with both the return value and the type.
Do you real need a method to call other methods? You could just instantiate the class and call it manually
In adittion i would recommend checking like so:
if(is_callable($className, $methodName)){
$className->$methodName();
}