If I understood your data structure well, here is a possible solution.
Use two years and generate a table for each year.
Code with a possible solution
<style>
td {
border: 1px solid grey;
padding: 5px;
}
</style>
<?php
/**
* [$key Año]
* [$key2 Semana]
* [$key3 Empleado]
* [$value3 Total horas]
*/
$arr = [
2017 => [
1 => [
11 => 45,
18 => 40,
28 => 42,
],
2 => [
11 => 19,
18 => 40,
28 => 37,
],
3 => [
11 => 36,
18 => 40,
28 => 34,
],
],
2018 => [
1 => [
11 => 41,
18 => 35,
28 => 42,
],
2 => [
11 => 19,
18 => 35,
28 => 10,
],
3 => [
11 => 36,
18 => 35,
28 => 38,
],
],
];
// Según la estructura de los datos voy a generar cada fila por
// separado y una tabla por año
// Contando con que para cada semana estén los mismos empleados
// Imprimo la primera fila directamente, las consecutivas
// (id empleado / horas en cada semana) requiere más trabajo
foreach ($arr as $year => $year_content) {
echo '<br><p>Year: ' . $year . '</p>';
?>
<table class="table table-condensed table-hover">
<tr> <!-- Opens header row (weeks) -->
<td>Employee</td>
<?php
$employees = [];
foreach ($year_content as $num_of_week => $week_content) {
echo '<td>WK' . $num_of_week . '</td>';
// Create an array with the employees' ids
$employees = array_keys($week_content);
}
// Prepare array to store an array of hours for each id
$employees = array_fill_keys($employees, []);
// echo '<pre>'; var_dump($employees); echo '</pre><br>'; // HACK: trace
foreach ($year_content as $week_content) {
$pos = 0;
foreach ($week_content as $employee_id => $hours) {
if ($employee_id === array_keys($employees)[$pos]) {
$employees[$employee_id][] = $hours;
}
$pos ++;
}
}
// echo '<pre>'; var_dump($employees); echo '</pre><br>'; // HACK: trace
//$employees esta listo para imprimir cada fila
foreach ($employees as $employee_id => $arr_hours) {
echo '<tr>'; // Opens employee row
echo '<td>emp' . $employee_id . '</td>';
foreach ($arr_hours as $hour) {
echo '<td>' . $hour . '</td>';
}
echo '</tr>'; // Closes employee row
}
?>
</tr> <!-- Closes header row (weeks) -->
</table>
<?php
} // end foreach (for year)
Result:
Edited: The same employees do not always work in the same weeks.
In a more realistic system , the employees who go to work every week of the year are not always the same, there may also be weeks in which no employee works.
Then the table will probably have empty cells and a "row of weeks" with the weeks in which you work and an "employee column" with the employees that work in at least one of those weeks, which may also be several or all of them.
The result would be something like this, for the table of a given year:
I will give a complete example (I have adjusted the arrays with the data for the new assumption):
<style>
td {
border: 1px solid grey;
padding: 5px;
}
</style>
<?php
$arr = [
2017 => [
1 => [
11 => 45,
28 => 42,
],
2 => [
28 => 37,
],
5 => [
11 => 36,
27 => 40,
32 => 34,
],
],
2018 => [
3 => [
27 => 35,
32 => 42,
],
4 => [
11 => 19,
18 => 35,
28 => 10,
],
],
];
// Según la estructura de los datos voy a generar una tabla por año.
// Se va a imprimir fila a fila.
// No siempre trabajan los mismos empleados en las mismas semanas.
// Los empleados que van a trabajar cada semana del año no son
// siempre los mismos, igualmente puede haber semanas en que ningún
// empleado trabaja. Entonces la tabla tendrá, probablemente,
// celdas vacías y una "fila de semanas" con las semanas en que se
// trabaja y una "columna de empleados" con los empleados que
// trabajan en al menos una de esas semanas, pudiendo ser asimismo
// varias o todas.
// Usaremos funciones para simplificar el problema
//
// Requiero todas las semanas trabajadas en el año
function getWeeks(array $year): array
{
return array_keys($year);
}
// Requiero todos los empleados que trabajan en el año
function getEmployeeIds(array $year): array
{
$employee_ids = [];
foreach ($year as $week_number => $employees_data) {
$employee_ids =
array_merge($employee_ids, array_keys($employees_data));
}
return array_unique($employee_ids);
}
// Usaremos funciones para imprimir los datos
//
function printHeaderRow(array $weeks): void
{
echo '<tr>';
echo '<td>Employee</td>';
foreach ($weeks as $week) {
echo '<td>WK' . $week . '</td>';
}
echo '</tr>';
}
function printEmployeeRow(
int $emp_id,
array $weeks,
array $data
): void
{
echo '<tr>';
echo '<td>emp' . $emp_id . '</td>';
// La impresión es horizontal por tanto comprobamos en cada
// semana si hay horas para el empleado actual
foreach ($weeks as $actual_week) {
foreach ($data as $analized_week => $employees_data) {
// La semana en los datos coincide con la actual:
// comprobaremos los empleados de la semana
if ($actual_week === $analized_week) {
$printed = false; // Controla si se han imprimido horas
foreach ($employees_data as $analized_id => $hours) {
// Se ha encontrado la id del empleado en los
// datos de la semana: imprimir horas
if ($emp_id === $analized_id) {
echo '<td>' . $hours . '</td>';
$printed = true;
}
}
// No se imprimiron horas: imprimir celda vacía
if (! $printed) {
echo '<td></td>';
}
}
}
}
echo '</tr>';
}
/**
* Prints a trace
*
* @param mixed $content
* @param string|null $msg
*/
function dx($content, string $msg=null): void
{
echo '<div style="border:1px solid grey;margin-bottom:10px;padding:10px">';
if ($msg) {
echo '<p>' . $msg . '</p>';
}
echo '<pre>';
var_dump($content);
echo '</pre>';
echo '</div>';
}
// Recorre los datos e imprime una tabla para cada año
foreach ($arr as $year => $year_data) {
echo '<br><p>Year: ' . $year . '</p>';
?>
<table class="table table-condensed table-hover">
<?php
$employee_ids = getEmployeeIds($year_data);
$weeks = getWeeks($year_data);
// HACK: trazas
// dx($employee_ids, 'IDs de los empleados');
// dx($weeks, 'Semanas en que se trabaja');
printHeaderRow($weeks);
foreach ($employee_ids as $employee_id) {
printEmployeeRow($employee_id, $weeks, $year_data);
} /**/
?>
</table>
<?php
} // end foreach (for year)
Result: