Create JournalDate model for setting per-date goals

This commit is contained in:
Christopher C. Wells 2021-05-15 08:59:53 -07:00 committed by Christopher Charbonneau Wells
parent 91fd85ef83
commit dc4d7987e8
5 changed files with 172 additions and 10 deletions

View File

@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* App\Models\JournalDate
*
* @property int $id
* @property \datetime $date
* @property int $user_id
* @property int|null $goal_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Goal|null $goal
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate query()
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate whereDate($value)
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate whereGoalId($value)
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|JournalDate whereUserId($value)
* @mixin \Eloquent
*/
final class JournalDate extends Model
{
use HasFactory;
/**
* @inheritdoc
*/
protected $fillable = [
'date',
];
/**
* @inheritdoc
*/
protected $casts = [
'date' => 'datetime:Y-m-d',
];
/**
* Get the Goal for this date.
*/
public function goal(): BelongsTo {
return $this->belongsTo(Goal::class);
}
/**
* Get the User this journal date belongs to.
*/
public function user(): BelongsTo {
return $this->belongsTo(User::class);
}
}

View File

@ -8,7 +8,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
@ -50,6 +49,8 @@ use Spatie\MediaLibrary\MediaCollections\Models\Media;
* @method static \Illuminate\Database\Eloquent\Builder|User whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|User withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug)
* @mixin \Eloquent
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\JournalDate[] $journalDates
* @property-read int|null $journal_dates_count
*/
final class User extends Authenticatable implements HasMedia
{
@ -98,6 +99,13 @@ final class User extends Authenticatable implements HasMedia
return $this->hasMany(Goal::class);
}
/**
* Get the User's journal dates.
*/
public function journalDates(): HasMany {
return $this->hasMany(JournalDate::class);
}
/**
* Get the User's journal entries.
*/
@ -107,13 +115,36 @@ final class User extends Authenticatable implements HasMedia
/**
* Get user's goal (if one exists) for a specific date.
*
* The primary use for a JournalDate entry right now is the goal so this
* method also creates a JournalDate if one does not already exist.
*/
public function getGoalByDate(Carbon $date): ?Goal {
/** @var \App\Models\JournalDate $journal_date */
$journal_date = $this->journalDates()->whereDate('date', '=', $date)->first();
if (empty($journal_date)) {
$journal_date = JournalDate::make(['date' => $date])->user()->associate(Auth::user());
}
if ($journal_date->goal) {
return $journal_date->goal;
}
// Check for a goal based on day of week configurations.
$day = Goal::days()->firstWhere('dow', $date->format('N'));
if (!$day) {
throw new \BadMethodCallException("No day with `dow` value {$date->format('N')}.");
}
return $this->goals()->whereRaw("(days & {$day['value']}) != 0")->get()->first();
/** @var \App\Models\Goal $goal */
$goal = $this->goals()->whereRaw("(days & {$day['value']}) != 0")->first();
if (!empty($goal)) {
$journal_date->goal()->associate($goal);
}
if ($journal_date->hasChanges(['date', 'goal'])) {
$journal_date->save();
}
return $goal;
}
/**

View File

@ -3,6 +3,7 @@
namespace Database\Factories;
use App\Models\Goal;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class GoalFactory extends Factory
@ -15,16 +16,18 @@ class GoalFactory extends Factory
/**
* {@inheritdoc}
*/
public function definition()
public function definition(): array
{
$from = $this->faker->dateTimeThisMonth;
$to = $this->faker->dateTimeBetween($from, '+1 year');
return [
'from' => $this->faker->randomElement([$from, null]),
'to' => $this->faker->randomElement([$to, null]),
'frequency' => $this->faker->randomElement(Goal::$frequencyOptions)['value'],
'name' => $this->faker->randomElement(Goal::getNameOptions())['value'],
'goal' => $this->faker->numberBetween(0, 2000),
'user_id' => User::factory(),
'name' => $this->faker->words,
'days' => $this->faker->randomElement(Goal::days()->pluck('value')->all()),
'calories' => $this->faker->numberBetween(1600, 2500),
'fat' => $this->faker->numberBetween(40, 90),
'cholesterol' => $this->faker->numberBetween(0, 500),
'sodium' => $this->faker->numberBetween(0, 3000),
'carbohydrates' => $this->faker->numberBetween(50, 100),
'protein' => $this->faker->numberBetween(90, 200),
];
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Database\Factories;
use App\Models\Goal;
use App\Models\JournalDate;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class JournalDateFactory extends Factory
{
/**
* {@inheritdoc}
*/
protected $model = JournalDate::class;
/**
* {@inheritdoc}
*/
public function definition(): array
{
return [
'date' => $this->faker->dateTimeThisMonth,
'user_id' => User::factory(),
'goal_id' => Goal::factory(),
];
}
}

View File

@ -0,0 +1,37 @@
<?php
use App\Models\Goal;
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJournalDatesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('journal_dates', function (Blueprint $table) {
$table->id();
$table->date('date')->useCurrent();
$table->foreignIdFor(User::class)->constrained()->cascadeOnUpdate()->cascadeOnDelete();
$table->foreignIdFor(Goal::class)->nullable()->constrained()->cascadeOnUpdate()->cascadeOnDelete();
$table->timestamps();
$table->unique(['date', 'user_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('journal_dates');
}
}