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;
}
Related
Good evening everyone,
I have some issues in my project. I'm trying to send mail to each collaborator who celebrates his birthday. The data are taken from my database. The problem is that: if I have for example 2 or 3 collaborators who celebrate their birthday in the same day, only the first in the list receive a mail. My question how send to all concerned collaborator a mail.
My code:
public function handle()
{
$collaborateurs = Card::listCard();
foreach ($collaborateurs as $collaborateur) {
return Mail::to($collaborateur->adresse_email)->send(new SendEmail($collaborateur));
}
}
class CarteAnnif {
public static function listCard(){
$collaborateurs = Organigramme::whereMonth('date_de_naissance', now()->month)
->whereDay('date_de_naissance', now()->day)
->get();
$collaborateursConcernes = [];
foreach ($collaborateurs as $collaborateur) {
$date_de_naissance = Carbon::createFromFormat('d/m/Y', $collaborateur->date_de_naissance)->format('d-m');
$date_de_naissance = strtotime($date_de_naissance);
$today = date('d-m');
$today = strtotime($today);
if($date_de_naissance == $today ){
$collaborateursConcernes[] = $collaborateur;
}
}
return collect($collaborateursConcernes);
}
}
public $collaborateur;
public function __construct($collaborateur)
{
$this->collaborateur = $collaborateur;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$this->view('admin.emails.send_card_to_collaborateurs')
->subject("Joyeux anniversaire");
return $this->from('app#domain.com')->view('admin.emails.send_card_to_collaborateurs');
}
}
I hope I have been clear otherwise I remain available for any other additional information
Thanks in advance!
The return ends a function, so your function will stop after the first loop. Just delete the retun :
foreach ($collaborateurs as $collaborateur) {
Mail::to($collaborateur->adresse_email)->send(new SendEmail($collaborateur));
}
i'm working on a project for my homework which i'm trying to make it work as a spa, but I've got some problem in making slug for posts.
in the tutorial which i've followed, instructor used this to make a slug from title :
protected static function boot()
{
parent::boot();
static::creating(function ($course){
$course->slug = str_slug($course->name);
});
}
now, if i make this table unique, which this is what i wanna do. how should i prevent app from giving me duplicate entry? or how can i add something to slug, like a number, Every time i get duplicate entry?
if i make a post with This Post name twice, second time, i get duplicate Error.
In my opinion your selected answer is not getting close to something efficient. In large applications the 2 random strings can be overwritten in a short time and then you will have huge issues (code and DB).
A safer approach is to build a service and use that when you save the slug in the DB. Or course this is not 100% perfect but definitely is better then to increment 2 random strings. That, by the way, can also affect the SEO part of the app.
Below you can find my example:
The Model
public static function boot()
{
parent::boot();
static::saving(function ($model) {
$slug = new Slug();
$model->slug = $slug->createSlug($model->title);
});
}
The Service
<?php
namespace App\Services;
use App\Job;
class Slug
{
/**
* #param $title
* #param int $id
* #return string
* #throws \Exception
*/
public function createSlug($title, $id = 0)
{
// Normalize the title
$slug = str_slug($title);
// Get any that could possibly be related.
// This cuts the queries down by doing it once.
$allSlugs = $this->getRelatedSlugs($slug, $id);
// If we haven't used it before then we are all good.
if (!$allSlugs->contains('slug', $slug)) {
return $slug;
}
// Just append numbers like a savage until we find not used.
for ($i = 1; $i <= 100; $i++) {
$newSlug = $slug . '-' . $i;
if (!$allSlugs->contains('slug', $newSlug)) {
return $newSlug;
}
}
throw new \Exception('Can not create a unique slug');
}
protected function getRelatedSlugs($slug, $id = 0)
{
return Model::select('slug')->where('slug', 'like', $slug . '%')
->where('id', '<>', $id)
->get();
}
}
You could use inbuilt Str class, and create some random strings in your Post slug. Example:
static::creating(function ($course){
$course->slug = str_slug($course->name . Str::random( 2 ));
});
This will add 2 random strings on each slug you create, which will ensure there are no duplicates. You can find more about Str class here.
you can use laravel Inbuilt Helper, follow below link.
https://laravel.com/docs/5.8/helpers#method-str-slug
$data = 'My Data'
$slug = Str::slug($data, '-');
dd($slug);
add this in your controller
use Illuminate\Support\Str;
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) ]
)
I'm trying the following: I have two models (Pub and Schedule) related by a 1xN relationship as follows:
Pub:
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function pubSchedules()
{
return $this->hasMany(Schedule::class);
}
Schedule:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function pub()
{
return $this->belongsTo(Pub::class);
}
Table schedules has the following fields:
id | pub_id | week_day | opening_time | closing_time |
I use the following function to know if one pub is currently (or not) open:
/**
* #return bool
*/
public function isPubCurrentlyOpen()
{
$schedules = Schedule::where([
['pub_id', $this->id ],
['week_day', Carbon::now()->dayOfWeek],
])->get();
foreach ($schedules as $schedule){
$isOpen[] =
Carbon::now('Europe/Madrid')->between(
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->opening_time),
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->closing_time)
);
}
if(in_array(true, $isOpen)){
return true;
//return "Pub Opened";
}
return false;
//return "Pub Closed";
}
In my PubController I'd like, when the option "Filter by open pubs" is chosen if($request->openPubs == 1), to show only opened pubs isOpen ==true.
Knowing the relationships between models, how can I do it?
I'm looking for something like this:
if($request->openPubs == 1)
{
$pubs = $pubs->with('pubSchedules')->where('isOpen' == true);
}
Can you help me?
Thanks a lot!
You can do this using a "whereHas"
$openPubs = Pub::whereHas('schedule', function ($query) {
$query->where('week_day', Carbon::now()->dayOfWeek);
$query->whereRaw(
"'".Carbon::now('Europe/Madrid')->format("H:i:s")."' BETWEEN opening_time AND closing_time"
);
})->get();
This is assuming your opening time and closing time are the appropriate time format and not strings (though strings will work as well in a 24h format).
You might achieve something similar to what you are looking for by using a scope e.g.
public function scopeFilterBy($query, $filter = null) {
if ($filter == "isOpen") {
$query->whereHas('schedule', function ($query) {
$query->where('week_day', Carbon::now()->dayOfWeek);
$query->whereRaw(
"'".Carbon::now('Europe/Madrid')->format("H:i:s")."' BETWEEN opening_time AND closing_time"
);
});
}
return $query; //Not sure if this is needed
}
You could then do:
Pub::filterBy($request->openPubs ? "isOpen" : null)->get();
I don't fully understand how you are trying to accomplish this but it should be something like this
$pubs = Pub::with(['pubSchedules' => function ($query) {
$query->where('opening_time', '>' ,Carbon::now()) // make sure it's currently open
->where('closing_time', '<' ,Carbon::now()) // make sure that it's not finished already
->where('week_day', '==' ,Carbon::now()->dayOfWeek) // make sure it's today
}])->find($id);
// to get if pub is currently
if($pub->pubSchedules->count()){
//
}
you can put this code in the model (Pub) and make some changes
if you already have the object you can do this (Add it to model)
public function isPubOpen()
{
$this->load(['pubSchedules' =>
// same code in other method
]);
return (bool) $this->pubSchedules->count();
}
For small tables you could call the function isPubCurrentlyOpen for each element.
For this you would need to change your function to recieve the pub_id as a parameter:
public function isPubCurrentlyOpen($pub_id)
{
$schedules = Schedule::where([
['pub_id', $pub_id ],
['week_day', Carbon::now()->dayOfWeek],
])->get();
foreach ($schedules as $schedule){
$isOpen[] =
Carbon::now('Europe/Madrid')->between(
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->opening_time),
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->closing_time)
);
}
if(in_array(true, $isOpen)){
return true;
//return "Pub Opened";
}
return false;
//return "Pub Closed";
}
and to query the data do:
if($request->openPubs == 1)
{
// assuming $pubs is a collection instance
$pubs = $pubs->filter(function($a){
return $this->isPubCurrentlyOpen($a->id);
})
}
There's a feature in Eloquent called Eager Loading. The Eloquent ORM provides a simple syntax to query for all the Schedules that are related with this particular Pub as described below:
$pubIsOpen= $pub->schedules()
->where([
['week_day', Carbon::now()->dayOfWeek],
['opening_time' , '<' , Carbon::now('Europe/Madrid')],
['closing_time' , '>' , Carbon::now('Europe/Madrid')]
])
->count();
if($openPubCount > 0){
//PUB is open
}else{
//PUB is closed
}
If it helps to someone in the future I post my solution, thanks to #apokryfos:
Pub:
/**
* #param $pubs
* #return mixed
*/
public static function isPubCurrentlyOpen($pubs)
{
$pubs->whereHas( 'pubSchedules', function ($pubs) {
$pubs->where( 'week_day', Carbon::now()->dayOfWeek )
->whereRaw(
"'" . Carbon::now( 'Europe/Madrid' )->format( "H:i:s" ) . "' BETWEEN opening_time AND closing_time"
);
} );
return $pubs;
}
PubsController:
/**
* #param GetPubRequest $request
* #return ApiResponse
*/
public function getPubs(GetPubRequest $request)
{
$orderBy = 'id';
$order = 'asc';
$pubs = Pub::withDistance();
............
if($request->openPubs == 1)
{
$pubs = Pub::isPubCurrentlyOpen($pubs);
}
return $this->response(PubProfileResource::collection($pubs->orderBy($orderBy, $order)->paginate()));
}
I have a user entity and I'm trying to update it from a UserService. The problem comes when I try to update a property which is set as an array collection.
/**
*
* #param \Doctring\Common\Collections\Collection $property
* #OneToMany(targetEntity="Countries",mappedBy="user", cascade={"persist", "remove"})
*/
private $countries;
I'm not sure if I'm supposed to somehow delete all the $countries before I set them back or if there's a way to select which ones to delete and set up the different ones....this is what my updateUser method looks so far:
public function updateUser($user) {
$entity = $this->getUser($user['id']);
if (!$entity)
throw new Exception('Error saving user!');
$countries = $this->getCountriesArray($user); //countries already set in the db
$tempCountries = array();
$delete = array();
foreach ($countries as $country) {
$found = false;
if (in_array($country, $user['countries'])) {
$tempCountries[] = $country;
} else {
$delete[] = $country;
}
}
$tempCountries = array_unique(array_merge( //combine the countries from the db we want to leave
$tempCountries, //with those the user selected
$user['countries']));
...
//here I need something to remove the countries in $delete...right?
...
$entity->setEmail($user['email']);
$entity->setResponsable($user['responsable']);
$entity->setPassword($this->createPass($user['password']));
$entity->setUrl($user['url']);
$entity->setRole($user['role']);
$modelCountries = array();
foreach($tempCountries as $c) {
$p = new Countries();
$p->setCountryName($c);
$p->setUser($entity);
$modelCountries[] = $p;
}
$entity->setCountries($modelCountries);
$this->em->persist($entity);
$this->em->flush();
}
please stackOverflow... give me a hand making sense out of this.
It actually depends on your use case.
As you said, you can either delete all countries before to set the new ones, or compute the delta, and update only the needed ones.
What countries are you providing to your service? The delta? Or full list?
Do you have performance constraints? If yes, what is the cost of a DELETE statements vs SELECT then UPDATE?
Compute delta, then UPDATE can be tricky and difficult, in most case, you better want to just DELETE all and INSERT.
For my current and personal choice I am using DQL to DELETE all owing side rows for the mapped entity and then inserting the new one.
class Rule
{
/**
* #OneToMany(targetEntity="Tier", mappedBy="rule", cascade={"persist", "remove"})
* #JoinColumn(name="ruleId", referencedColumnName="ruleId")
* #var Tier[]
*/
public $tiers;
So when I am passing new Tier in my call I am simply Deleting the all Tiers for that Role from the Rule Mapper
public function resetTiers($ruleId)
{
$modelClass = "Tier";
$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();
return $numDeleted;
}
and then calling the usual
$this->doctrineEntityManager->persist($rule);
$this->doctrineEntityManager->flush();
Hope that helps