每天从带有cron作业的数组中获取3个项目

i have a small PHP Cron Job that runs every day get a file from an API and save it to a static file.

file_put_contents("api.json", fopen("http://example.com/api", 'r'));

The content of this JSON looks like that:

{ 
  recipes: [
  {
    id: 30476,
    title: "Selfmade Chicken Nuggets",
    ...
  }, 
  {...} ] 
}

My issue: I would like to create a "recipes of the day" logic.

Therefore i would like to create an extra array with recipes per day.

In best case I would like to have something like this:

Step 1: Create a "remaining recipes array" that contains all recipes

Step 2: Get 3 recipes per day from the remaining recipes array and put them in some kind of "recipes of the day"-array

Step 3: If the remaining recipes array is empty or don't have 3 elements, refill it from recipes

I already have that logic in my Javascript client:

let fullRecipeList = await this.appData.getRecipeList();
let recipesOfTheDay = await this.appData.getItem("recipesOfTheDay");
let recipesOfTheDayValidUntil = await this.appData.getItem(
    "recipesOfTheDayValidUntil"
);
let remainingRecipes = await this.appData.getItem("remainingRecipes");

if (!remainingRecipes || remainingRecipes.length < 3) {
    remainingRecipes = this.shuffleArray(fullRecipeList);
}

if (
  recipesOfTheDay &&
  moment(recipesOfTheDayValidUntil).isSame(new Date(), "day")
) {
    this.recipeList = recipesOfTheDay;
} else {
    recipesOfTheDay = remainingRecipes.splice(0, 3);
    this.recipeList = recipesOfTheDay;
    await this.appData.setItem("remainingRecipes", remainingRecipes);
    await this.appData.setItem("recipesOfTheDay", recipesOfTheDay);
    await this.appData.setItem(
        "recipesOfTheDayValidUntil",
        moment().startOf("day")
    );
}

Is it possible to create that kind of logic in my server side cron job?

How would the code look like? I am quite new to the whole PHP world :)

Sometimes if you use php directly in cron, the environmental context is different and there can be issues with files: files can't be found for reading, or files can be written in strange places.

What I've done in my cron jobs is call web services directly using wget or similar utility:

# every day at eight run recipes of the day
* 8 * * * wget -q -O /dev/null 'https://www.server.com/recipesOfTheDay.php'

You can even use localhost as your server name.

Your script can then save to a local JSON file or whatever with the revised contents.

Something like this?

<?php

$totalRecipesPerDay = 3;

// Open remaining receipes. If the file does not exists, return an empty array
$remainingRecipes = file_exists('remaining_recipes.json') ? json_decode(file_get_contents('remaining_recipes.json'), true) : [];

// Check if atleast $totalRecipesPerDay are available
if (count($remainingRecipes) < $totalRecipesPerDay) {
    // Insufficient receipes, getting it from the complete list
    $remainingRecipes = json_decode(file_get_contents('all_recipes.json'), true);

    // Shuffling results
    shuffle($remainingRecipes);
}

$recipesOfTheDay = [];
for ($i = 0; $i < $totalRecipesPerDay; ++$i) {
    // Extracting n times a recipe from my remaining list, and update it in the meantime
    $recipesOfTheDay[] = array_shift($remainingRecipes);
}

// Save results :D
file_put_contents('remaining_recipes.json', json_encode($remainingRecipes));
file_put_contents('recipes_of_the_day.json', json_encode($recipesOfTheDay));


Then set up a cronjob using crontab -e:

* * * * * php /var/www/your_project/file.php

Here the instructions:

https://www.ostechnix.com/wp-content/uploads/2018/05/cron-job-format-1.png

Not tested. See comments below script.

if ( ! file_exists('remaining-recipes.json')  // e.g. on first ever run of script
   $reminingRecipeArray = renew_remaining_recipes_file();
}
else {
   $reminingRecipeArray = json_decode(file_get_contents('remaining-recipes.json'), true);
   if count($reminingRecipeArray) < 3 ) renew_remaining_file();
}

// equivalent to your javascript example
$recipesOfTheDayArray = array[];
shuffle($reminingRecipeArray);
for ($i=0; $i < 2; $i++) {
  $recipesOfTheDayArray[i] = array_pop($reminingRecipeArray);
 // n.b. pop gets/removes element from array end - see my answer
}

file_put_contents('recipes_of_the_day.json', json_encode($recipesOfTheDayArray));
file_put_contents('remaining_recipes.json', json_encode($reminingRecipeArray));

//----------------
//***Functions****
//----------------

function renew_remaining_recipes_file() {
  if ( ! file_exists('allrecipes.json') ) {
    get_all_recipes_file(); // remove surrounding "if" to always refresh via API whenever remaining list empty
  }
  $reminingRecipeArray = json_decode(file_get_contents('allrecipes.json'), true);
  if (empty( $reminingRecipeArray) || count($reminingRecipeArray) < 3 ) {  // on fail or empty array
    email_and_exit('could not refresh remaining recipe list - maybe allrecipes file is not json');
  }
  return $reminingRecipeArray;
}

function get_all_recipes_file() {
  // uses yor current code which I assume works
  $allOK = file_put_contents("allrecipes.json", fopen("http://example.com/api", 'r'));
  // $allOK is FALSE (0) on failure otherwise number of bytes in file
  if ($allOK < 500) {
    email_and_exit('Failed to create "allrecipes.json" or created file or too small for expected data');
  }
}

DEFINE("TESTING", TRUE);
function email_and_exit($msg) {
  if (TESTING) echo $msg; // additionally echo msg to screen when testing
  mail(YOUR_EMAIL_ADDRESS, 'Recipe Error', $msg, 'From: yourpage@YOURSITE');
  exit;
}

The above code uses array_pop to take 3 items from the end of the array of remaining recipes. If you wish to take recipes from start of array use array _shift but its less efficient https://www.php.net/manual/en/function.array-pop.php#112946

Your visitors may be in different timezones so if they return 12 hours later on (their) following day they may be presented with the same recipe. This can be solved solely with PHP but from recollection, when working with a weather API, this can be tricky if unfamiliar with PHP. You could cater for this with a mix of PHP and JS creating and cycling 3(9?) recipe of day entries (or 3 files suffixed by day number)

Day number from PHP;

$serverDay = idate('w', $timestamp);  // day of week as integer (Sunday=0);
$dayafter = $intDay + 1;
$daybefore = $intDay - 1;

Then use number from getDay() in your JavaScript call to tell server which day should be provided.