I'm stuck I want to create a Laravel Nova Metrics Trend where it goes in the future instead of back in time.
This code will output: 15-March-2019, 14-March-2019, 13-March-2019,
public function calculate(Request $request)
{
return $this->sumByDays($request, Payment::class, 'hours_per_month', 'upcoming_invoice_date')->showLatestValue();
}
/**
* Get the ranges available for the metric.
*
* #return array
*/
public function ranges()
{
return [
3 => '3 Days',
];
}
I've already made some changes to Trend.php but it gave me nothing than errors.
/**
* Return a value result showing a sum aggregate over days.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Database\Eloquent\Builder|string $model
* #param string $column
* #param string $dateColumn
* #return \Laravel\Nova\Metrics\TrendResult
*/
public function sumByDays($request, $model, $column, $dateColumn = null)
{
return $this->aggregate($request, $model, Trend::BY_DAYS, 'sum', $column, $dateColumn);
}
Is this even possible?
Thanks,
The trend range is defined in Trend.php so you were on the right track!
Check out the protected function aggregate.
protected function aggregate($request, $model, $unit, $function, $column, $dateColumn = null)
{
The $endingDate can be changed to whatever you want!
$possibleDateResults = $this->getAllPossibleDateResults(
$startingDate = $this->getAggregateStartingDate($request, $unit),
$endingDate = Chronos::now();
The Chronos API has nice documentation but try something like this
$endingDate = Chronos::tomorrow()->addWeeks(4),
Note: I have done very minimal testing on this so use at your own risk. It does break the showLatestValue() method for trend metrics, but that could be addressed in TrendResult.php if you really needed it.
1) You can change the range dates to the future with a negative number (of days) as key:
public function ranges()
{
return [
-3 => 'Next 3 Days',
];
}
The problem is that it doesn't work because the order of datetimes will be wrong in the query as the endDate is always set to now() in Trend.php:
$startingDate = $this->getAggregateStartingDate($request, $unit),
$endingDate = Chronos::now(),
...
->whereBetween($dateColumn, [$startingDate, $endingDate])
2) Quick/dirty fix. In Trend.php change the whereBetween() second argument array (of datetimes) comparing and setting datetimes in ascending order.
->whereBetween(
$dateColumn,
[ min($startingDate, $endingDate), max($startingDate, $endingDate) ]
)
Related
Out of the blue suddenly my custom scopes in Laravel are no longer working. Somehow when used it's failing to separate additional parameters given from the $query parameter, clumping them into single array.
This results for instance a simple scope like the following failing because "Call to a member function whereNull() on array"
// WHERE IS ACTIVE
public function scopeWhereIsActive($query)
{
return $query->whereNull('show_until')->where('is_old', false)
->orWhere('show_until', '>', now())->where('is_old', false)
->orderByDesc('created_at');
}
And if I use a custom scope with additional parameters like the following, I get a missing arguments error (1 passed at least 2 expected). The thing however is that it worked perfectly just before, and I can't find any changes made that would affect this.
/** WHERE HAS PRODUCT
*
* #param \Illuminate\Database\Eloquent\Builder
* #param App\Models\Product $product
* #param boolean|false $return_with_loaded_products
* #param array $included_number_type_sums_array
* #return void
*/
public function scopeWhereHasProduct($query, $product, $return_with_loaded_products = false, $included_number_type_sums_array = null)
{
// dd('Order#scopeWherehasProduct parameters', $query, $product, $return_with_loaded_products , $included_number_type_sums_array);
$query->whereHas('products', function($query) use ($product) {
$query->where('product_id', $product->id);
});
if (isset($included_number_type_sums_array)) {
foreach ($included_number_type_sums_array as $type) {
$query->withSum(['products' => function($query) use($product) {
$query->where('product_id', $product->id);
}], 'int_orders_products.number_' . $type);
}
}
if ($return_with_loaded_products) {
$query->with(['products' => function($query) use($product) {
$query->where('product_id', $product->id);
}]);
}
return $query;
}
The $parameters parameter for the Laravel function calling the scope in Illuminate/Database/Eloquent.Model.php is a standard array, which I think should be correct, but the problem is that it's not being then split up across the separate parameter variables somehow.
/**
* Apply the given named scope if possible.
*
* #param string $scope
* #param array $parameters
* #return mixed
*/
public function callNamedScope($scope, $parameters = [])
{
dd('halt',$parameters);
return $this->{'scope'.ucfirst($scope)}($parameters);
}
This returns for the first scope:
^ array:1 [▼
0 => Illuminate\Database\Eloquent\Builder {#1877 ▼
#query: Illuminate\Database\Query\Builder {#1864 ▶}
#model: App\Models\Announcement {#1923 ▶}
#eagerLoad: []
#localMacros: []
#onDelete: null
#propertyPassthru: array:1 [▶]
#passthru: array:21 [▶]
#scopes: []
#removedScopes: []
}
]
I haven't touched anything 'deep' in the Laravel code that I know off, and rolling back isn't helping. My guess is that something deep in Laravel itself got wonked, but I really don't know where to look for these kind of issues. The laravel version is 9.15.00
I never could confirm what the problem was, apparently it could have been an altered `callNamedScope' function. I solved the issue by upgrading to the latest Laravel version (9.54.00).
Laravel Nova suggests Value Metrics and the ranges are for previous days until today. It is okay while we are using created_at as the default date column to show the result.
But, sometimes we need to show the result based on a date column that is able to contain a value for later days. For example, we may need to calculate the sum amount of a resource based on its settlement_date which might be tomorrow.
/**
* Calculate the value of the metric.
*
* #param NovaRequest $request
* #return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->sum(
$request,
Settlement::where('status', 'PENDING'), 'amount', 'settlement_date')->format('0,0');
}
/**
* Get the ranges available for the metric.
*
* #return array
*/
public function ranges()
{
return [
'TODAY' => 'Today',
7 => 'Week',
30 => '30 Days',
60 => '60 Days',
365 => '365 Days',
];
}
WHAT IF, I want to know the value of this query for the later days like tomorrow.
Something like this query does not work, any idea?
return $this->sum(
$request,
Settlement::where('status', 'PENDING')->where('settlement_date', Carbon::tomorrow()), 'amount', 'settlement_date')->format('0,0');
I solved it using Trends Metric, like this:
/**
* Calculate the value of the metric.
*
* #param \Laravel\Nova\Http\Requests\NovaRequest $request
* #return mixed
*/
public function calculate(NovaRequest $request)
{
$settlement = new \App\Models\VmsSettlement\Settlement;
$twoDaysAgoSum = $settlement->where('status', 'PENDING')->where('settlement_date', Carbon::today()->subDays(2)->toDateString())->sum('amount');
$yesterdaySum = $settlement->where('status', 'PENDING')->where('settlement_date', Carbon::today()->subDay()->toDateString())->sum('amount');
$todaySum = $settlement->where('status', 'PENDING')->where('settlement_date', Carbon::today()->toDateString())->sum('amount');
$tomorrowSum = $settlement->where('status', 'PENDING')->where('settlement_date', Carbon::today()->addDay()->toDateString())->sum('amount');
$twoDaysLaterSum = $settlement->where('status', 'PENDING')->where('settlement_date', Carbon::today()->addDays(2)->toDateString())->sum('amount');
return (new TrendResult)->trend([
Carbon::today()->subDays(2)->toDateString() . ' (2 days ago)' => $twoDaysAgoSum,
Carbon::today()->subDay()->toDateString() . ' (Yesterday)' => $yesterdaySum,
Carbon::today()->toDateString() . ' (Today)' => $todaySum,
Carbon::today()->addDay()->toDateString() . ' (Tomorrow)' => $tomorrowSum,
Carbon::today()->addDays(2)->toDateString() . ' (2 days later)' => $twoDaysLaterSum,
])->format('0,0');
}
/**
* Get the ranges available for the metric.
*
* #return array
*/
public function ranges()
{
return [
//30 => '30 Days',
//60 => '60 Days',
//90 => '90 Days',
];
}
I have the following table:
Active
id
starts_at
ends_at
I would like to get all the actives daily, comparing the two dates starts_at and ends_at and get the diff in days, like this example:
Route::get('test', function(){
$dailyActives = \App\Models\Active::all()->filter(function ($active) {
return $active->starts_at->diffInDays($active->ends_at) >= 1 && $active->starts_at->diffInDays($active->ends_at) <= 3;
});
dd($dailyActives);
});
it works 100%.
But I would like to reuse this code as I have more modes like Daily, Weekly, Month.
My idea was creating a scope in the model but I can't use filter as $query is not a collection.
I tried the following code:
/**
* Scope a query to only include daily actives.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeDaily($query)
{
$query->filter(function ($active) {
if($active->starts_at->diffInDays($active->ends_at) >= 1 && $active->starts_at->diffInDays($active->ends_at) <=3) {
return true;
}
});
}
So can someone recommend me the best way to do that? Maybe using scope and how? or creating a reusable class, where just calling the Active::daily()->get() I get all the actives daily, for example.
Many thanks in advance!
You do not need to use a filter. Use scop like this $dailyActives = \App\Models\Active::interval('weekly')->get();
public function scopeInterval($query, $interval = 'daily')
{
// daily
$dateBetween = [now()->startOfDay(), now()->endOfDay()];
if($interval === 'weekly'){
$dateBetween = [now()->startOfWeek(), now()->endOfWeek()];
}
elseif($interval === 'month'){
$dateBetween = [now()->startOfMonth(), now()->endOfMonth()];
}
$query->whereBetween('created_at', $dateBetween);
return $query;
}
Trying to run an API which will give me the updated temperature and humidity values but the curl function is not working as it gives a NULL reponse and throws error. running the from the terminal to test it
code:
class updateTempHumHourly extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'update:temphum';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Update temperature and humidity readings hourly';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$options = array(
'cluster' => 'ap2',
'useTLS' => true
);
$pusher = new \Pusher\Pusher(
'osdifnerwion4iownfowinf',
'dofhewofhewofnowenfid',
'7asdkland',
$options
);
$thinkers = t::where('user_id', '!=' , NULL)->where('thinker_status',1)->get();
foreach($thinkers as $t)
{
$temp = [
'action'=>'list_slave',
'mac'=> $t->thinker_MAC,
'type' => 'all',
'appkey' => 'nope'
];
json_encode($temp);
$response = Curl::to('http://103.31.82.46/open/open.php')
->withContentType('application/json')
->withData($temp)
->asJson(true)
->withHeader('Postman-Token: d5988618-676e-430c-808e-7e2f6cec88fc')
->withHeader('cache-control: no-cache')
->post();
foreach($response['slaves'] as $s)
{
if(array_key_exists("temp",$s) && array_key_exists("hum",$s))
{
$slave = sd::where("connected_thinker_MAC",$response['mac'])->where("device_id",$s['slave_id'])->first();
$slave->temperature = $s['temp'];
$slave->humidity = $s['hum'];
$slave->save();
$array = [];
$array['temperature'] = $s['temp'];
$array['humidity'] = $s['hum'];
$array['id'] = $s['slave_id'];
$pusher->trigger($t->user_id."-channel","s-event",$array);
\App\helper\log::instance()->tempHumLog($s);
}
}
}
}
}
the foreach loop throws an error that $response is equal to null. the curl function is not working from here but working fine regularly. help. i need to run this task every hour to get the average temperature and humidity.
Since you said that your code works somewhere that means that there is something wrong with your server and not the code itself probably (since it's a CURL issue).
Inside your php.ini file there is this line : extension=php_curl.dll to enable curl. Uncomment it on your server that you have the crons to make curl work. This should solve your problem.
Testing localy the api you provide us returns a 500 but if you say that it works for you i assume that this is just an example and the problem is in CURL itself.
do anyone know how can I apply rule in Yii model for input must be greater than 0 value, without any custom approach ..
like :
public function rules()
{
return array(
....
....
array('SalePrice', 'required', "on"=>"sale"),
....
....
);
}
many thanks ..
Simpler way
array('SalePrice', 'numerical', 'min'=>1)
with a custom validator method
array('SalePrice', 'greaterThanZero')
public function greaterThanZero($attribute,$params)
{
if ($this->$attribute<=0)
$this->addError($attribute, 'Saleprice has to be greater than 0');
}
I see it is a price so you could use 0.01 (a penny) as a minimum value like so:
array('SalesPrice', 'numerical', 'min'=>0.01),
Note that this solution does not validate that the number entered is a price, just that it is > 0.01
I know I am too late for this . But just for future reference you can use this class also
<?php
class greaterThanZero extends CValidator
{
/**
* Validates the attribute of the object.
* If there is any error, the error message is added to the object.
* #param CModel $object the object being validated
* #param string $attribute the attribute being validated
*/
protected function validateAttribute($object,$attribute)
{
$value=$object->$attribute;
if($value <= 0)
{
$this->addError($object,$attribute,'your password is too weak!');
}
}
/**
* Returns the JavaScript needed for performing client-side validation.
* #param CModel $object the data object being validated
* #param string $attribute the name of the attribute to be validated.
* #return string the client-side validation script.
* #see CActiveForm::enableClientValidation
*/
public function clientValidateAttribute($object,$attribute)
{
$condition="value<=0";
return "
if(".$condition.") { messages.push(".CJSON::encode($object->getAttributeLabel($attribute).' should be greater than 0').");
}";
}
}
?>
Just make sure this class is imported before use.
Did nobody check the docs?
There is a built-in validator CCompareValidator:
['SalePrice', 'compare', 'operator'=>'>', 'compareValue'=>0]
you can use this one too:
array('SalePrice', 'in','range'=>range(0,90))
I handled this by regular expression, may be it will help too ..
array('SalePrice', 'match', 'not' => false, 'pattern' => '/[^a-zA-Z0]/', 'message' => 'Please enter a Leader Name', "on"=>"sale"),
many thanks #sdjuan & #Ors for your help and time ..