问:
Selecting multiple tables for a Query MySQLi PHP
I am trying to create a query, but I don't know how to select multiple tables with the way I have my database set up.

In my query I am trying to display what movies have what actor, but each movie has multiple actors so I have to select from three tables.
This is my movies table:

This is my actor_information table

My last table is the actor_movie table. This table correlates the movie_id with and actor_id

I have the following php to connect to the database, but I am not sure how to write the query portion.

```<code><?php
$databaseName = 'movie_database';
$databaseUser = 'root';
$databasePassword = 'root';
$databaseHost = '127.0.0.1';

$conn = new mysqli($databaseHost, $databaseUser, $databasePassword, $databaseName);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

echo "Connected successfully <br>";

// this code is wrong, but i get the general idea of how to select from my db.
$sql = "SELECT actor_movie, movies, actor_information FROM actor_movie INNER
JOIN movies ON actor_movie.movie_id = movies.movie_id
INNER JOIN actor_information ON actor_movie.actor_id =
actor_information.actor_id"
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
    // output data of each row
    while($row = mysqli_fetch_assoc($result)) {
        echo " Title " . $row["title"]. "". $row["actor_id"]. "<br>";
    }
} else {
    echo "0 results";
}

mysqli_close($conn);
?>
</code>```

Here is my uml if it helps:

答:
As @tadman suggested, the object-oriented mysqli is better. My recommendation, though, is to use PDO instead of mysqli. Read this.

Read here and here to properly handle the errors/exceptions.

Note the use of the prepared statement (see here too) in order to avoid SQL injection.

You can also fetch just one movie.

The select uses LEFT JOINs: fetches all movies and "attaches" the actor details to each of them, by using actor_movie. There are also the movies fetched, that doesn't have any actors assigned yet (e.g. actor_name = NULL in result set).

After the result set is fetched, I built a 2nd array ($movies) to prepare the data to be properly displayed in the html table. Note that this step could be replaced by fetching data from db using multiple queries: one for fetching the movies list, and a query for each movie for fetching the actors.

Note the separation of data fetching code (php, upper page part) from the displaying code (html, lower page part). E.g: all data is fetched from db in arrays (php, upper page part) and only these arrays are then iterated in order to display the data (html, lower page part). E.g: no data fetching code is mixed with the data displaying code.

The db structure corresponds to the one kindly presented by you.

Notice the "use" of actor "Robert de Niro" (actor_id = 1) in two movies (see actor_movie table).

Good luck.

Note: all three solutions are identical, starting with the line: $movies = [];

Solution 1: mysqli using get_result() + fetch_all()

Works only if the mysqlnd driver ("MySQL Native Driver") is installed!

```<code><?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');

// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!

/**
 * Enable internal report functions. This enables the exception handling,
 * e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
 * (mysqli_sql_exception). They are catched in the try-catch block.
 *
 * MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
 * MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
 *
 * See: http://php.net/manual/en/class.mysqli-driver.php
 * See: http://php.net/manual/en/mysqli-driver.report-mode.php
 * See: http://php.net/manual/en/mysqli.constants.php
 */
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/**
 * Create a new db connection.
 *
 * @see http://php.net/manual/en/mysqli.construct.php
 */
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);

// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;

/*
 * The SQL statement to be prepared. Notice the so-called markers,
 * e.g. the "?" signs. They will be replaced later with the
 * corresponding values when using mysqli_stmt::bind_param.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$sql = sprintf(
        'SELECT
                mv.movie_id,
                mv.title,
                mv.rating,
                mv.Runtime,
                mv.movie_rating,
                mv.release_date,
                acin.actor_name
            FROM movies AS mv
            LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
            LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
            %s'
        , isset($movieId) ? 'WHERE mv.movie_id = ?' : ''
);

/*
 * Prepare the SQL statement for execution - ONLY ONCE.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Bind variables for the parameter markers (?) in the
 * SQL statement that was passed to prepare(). The first
 * argument of bind_param() is a string that contains one
 * or more characters which specify the types for the
 * corresponding bind variables.
 *
 * See: http://php.net/manual/en/mysqli-stmt.bind-param.php
 */
if (isset($movieId)) {
    $statement->bind_param('i', $movieId);
}

/*
 * Execute the prepared SQL statement.
 * When executed any parameter markers which exist will
 * automatically be replaced with the appropriate data.
 *
 * See: http://php.net/manual/en/mysqli-stmt.execute.php
 */
$executed = $statement->execute();

/*
 * Get the result set from the prepared statement.
 *
 * NOTA BENE:
 * Available only with mysqlnd ("MySQL Native Driver")! If this
 * is not installed, then uncomment "extension=php_mysqli_mysqlnd.dll" in
 * PHP config file (php.ini) and restart web server (I assume Apache) and
 * mysql service. Or use the following functions instead:
 * mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
 *
 * See:
 *      http://php.net/manual/en/mysqli-stmt.get-result.php
 *      https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$result = $statement->get_result();

/*
 * Fetch data and save it into $fetchedData array.
 *
 * See: http://php.net/manual/en/mysqli-result.fetch-all.php
 */
// Fetch all rows at once...
$fetchedData = $result->fetch_all(MYSQLI_ASSOC);

// ... OR fetch one row at a time.
// while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
//     $fetchedData[] = $row;
// }
//
// Just for testing. Display fetched data.
//echo '```' . print_r($fetchedData, TRUE) . '```';

/*
 * Free the memory associated with the result. You should
 * always free your result when it is not needed anymore.
 *
 * See: http://php.net/manual/en/mysqli-result.free.php
 */
$result->close();

/*
 * Close the prepared statement. It also deallocates the statement handle.
 * If the statement has pending or unread results, it cancels them
 * so that the next query can be executed.
 *
 * See: http://php.net/manual/en/mysqli-stmt.close.php
 */
$statementClosed = $statement->close();

// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
    $movieId = $item['movie_id'];
    $title = $item['title'];
    $rating = $item['rating'];
    $runtime = $item['Runtime'];
    $movieRating = $item['movie_rating'];
    $releaseDate = $item['release_date'];
    $actorName = $item['actor_name'];

    if (!array_key_exists($movieId, $movies)) {
        $movies[$movieId] = [
            'title' => $title,
            'rating' => $rating,
            'Runtime' => $runtime,
            'movie_rating' => $movieRating,
            'release_date' => $releaseDate,
        ];
    }

    if (isset($actorName)) {
        $movies[$movieId]['actors'][] = $actorName;
    } else {
        $movies[$movieId]['actors'] = [];
    }
}

// Just for testing. Display movies list.
//echo '```' . print_r($movies, TRUE) . '```';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
            .movies { border-collapse: collapse; border: 1px solid #ccc; }
            .movies td, th { padding: 5px; }
            .movie-record { color: white; background-color: #00b3ee; }
            .actor-record { background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <h3>
            Movies list
        </h3>

        <table class="movies">
            <thead>
                <tr>
                    <th>Movie ID</th>
                    <th>Title</th>
                    <th>Rating</th>
                    <th>Runtime</th>
                    <th>Movie Rating</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($movies) {
                    foreach ($movies as $movieId => $movie) {
                        $title = $movie['title'];
                        $rating = $movie['rating'];
                        $runtime = $movie['Runtime'];
                        $movieRating = $movie['movie_rating'];
                        $releaseDate = $movie['release_date'];
                        ?>
                        <tr class="movie-record">
                            <td><?php echo $movieId; ?></td>
                            <td><?php echo $title; ?></td>
                            <td><?php echo $rating; ?></td>
                            <td><?php echo $runtime; ?></td>
                            <td><?php echo $movieRating; ?></td>
                            <td><?php echo $releaseDate; ?></td>
                        </tr>
                        <?php
                        foreach ($movie['actors'] as $actorName) {
                            ?>
                            <tr class="actor-record">
                                <td colspan="6"><?php echo $actorName; ?></td>
                            </tr>
                            <?php
                        }
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="6">
                            <?php echo 'No movies found'; ?>
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>
</code>```

Solution 2: mysqli using store_result() + bind_result() + fetch()

Hard to work with. But it works even if the mysqlnd driver ("MySQL Native Driver") is not installed.

```<code><?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');

// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!

/**
 * Enable internal report functions. This enables the exception handling,
 * e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
 * (mysqli_sql_exception). They are catched in the try-catch block.
 *
 * MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
 * MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
 *
 * See: http://php.net/manual/en/class.mysqli-driver.php
 * See: http://php.net/manual/en/mysqli-driver.report-mode.php
 * See: http://php.net/manual/en/mysqli.constants.php
 */
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/**
 * Create a new db connection.
 *
 * @see http://php.net/manual/en/mysqli.construct.php
 */
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);

// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;

/*
 * The SQL statement to be prepared. Notice the so-called markers,
 * e.g. the "?" signs. They will be replaced later with the
 * corresponding values when using mysqli_stmt::bind_param.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$sql = sprintf(
        'SELECT
                mv.movie_id,
                mv.title,
                mv.rating,
                mv.Runtime,
                mv.movie_rating,
                mv.release_date,
                acin.actor_name
            FROM movies AS mv
            LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
            LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
            %s'
        , isset($movieId) ? 'WHERE mv.movie_id = ?' : ''
);

/*
 * Prepare the SQL statement for execution - ONLY ONCE.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Bind variables for the parameter markers (?) in the
 * SQL statement that was passed to prepare(). The first
 * argument of bind_param() is a string that contains one
 * or more characters which specify the types for the
 * corresponding bind variables.
 *
 * See: http://php.net/manual/en/mysqli-stmt.bind-param.php
 */
if (isset($movieId)) {
    $statement->bind_param('i', $movieId);
}

/*
 * Execute the prepared SQL statement.
 * When executed any parameter markers which exist will
 * automatically be replaced with the appropriate data.
 *
 * See: http://php.net/manual/en/mysqli-stmt.execute.php
 */
$executed = $statement->execute();

/*
 * Transfer the result set resulted from executing the prepared statement.
 * E.g. store, e.g. buffer the result set into the (same) prepared statement.
 *
 * See:
 *      http://php.net/manual/en/mysqli-stmt.store-result.php
 *      https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$resultStored = $statement->store_result();

/*
 * Bind the result set columns to corresponding variables.
 * E.g. these variables will hold the column values after fetching.
 *
 * See: http://php.net/manual/en/mysqli-stmt.bind-result.php
 */
$varsBound = $statement->bind_result(
        $boundMovieId
        , $boundTitle
        , $boundRating
        , $boundRuntime
        , $boundMovieRating
        , $boundReleaseDate
        , $boundActorName
);

/*
 * Fetch results from the result set (of the prepared statement) into the bound variables.
 *
 * See: http://php.net/manual/en/mysqli-stmt.fetch.php
 */
$fetchedData = [];
while ($row = $statement->fetch()) {
    $fetchedData[] = [
        'movie_id' => $boundMovieId,
        'title' => $boundTitle,
        'rating' => $boundRating,
        'Runtime' => $boundRuntime,
        'movie_rating' => $boundMovieRating,
        'release_date' => $boundReleaseDate,
        'actor_name' => $boundActorName,
    ];
}

// Just for testing. Display fetched data.
//echo '```' . print_r($fetchedData, TRUE) . '```';

/*
 * Frees the result memory associated with the statement,
 * which was allocated by mysqli_stmt::store_result.
 *
 * See: http://php.net/manual/en/mysqli-stmt.store-result.php
 */
$statement->free_result();

/*
 * Close the prepared statement. It also deallocates the statement handle.
 * If the statement has pending or unread results, it cancels them
 * so that the next query can be executed.
 *
 * See: http://php.net/manual/en/mysqli-stmt.close.php
 */
$statementClosed = $statement->close();

// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
    $movieId = $item['movie_id'];
    $title = $item['title'];
    $rating = $item['rating'];
    $runtime = $item['Runtime'];
    $movieRating = $item['movie_rating'];
    $releaseDate = $item['release_date'];
    $actorName = $item['actor_name'];

    if (!array_key_exists($movieId, $movies)) {
        $movies[$movieId] = [
            'title' => $title,
            'rating' => $rating,
            'Runtime' => $runtime,
            'movie_rating' => $movieRating,
            'release_date' => $releaseDate,
        ];
    }

    if (isset($actorName)) {
        $movies[$movieId]['actors'][] = $actorName;
    } else {
        $movies[$movieId]['actors'] = [];
    }
}

// Just for testing. Display movies list.
//echo '```' . print_r($movies, TRUE) . '```';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
            .movies { border-collapse: collapse; border: 1px solid #ccc; }
            .movies td, th { padding: 5px; }
            .movie-record { color: white; background-color: #00b3ee; }
            .actor-record { background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <h3>
            Movies list
        </h3>

        <table class="movies">
            <thead>
                <tr>
                    <th>Movie ID</th>
                    <th>Title</th>
                    <th>Rating</th>
                    <th>Runtime</th>
                    <th>Movie Rating</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($movies) {
                    foreach ($movies as $movieId => $movie) {
                        $title = $movie['title'];
                        $rating = $movie['rating'];
                        $runtime = $movie['Runtime'];
                        $movieRating = $movie['movie_rating'];
                        $releaseDate = $movie['release_date'];
                        ?>
                        <tr class="movie-record">
                            <td><?php echo $movieId; ?></td>
                            <td><?php echo $title; ?></td>
                            <td><?php echo $rating; ?></td>
                            <td><?php echo $runtime; ?></td>
                            <td><?php echo $movieRating; ?></td>
                            <td><?php echo $releaseDate; ?></td>
                        </tr>
                        <?php
                        foreach ($movie['actors'] as $actorName) {
                            ?>
                            <tr class="actor-record">
                                <td colspan="6"><?php echo $actorName; ?></td>
                            </tr>
                            <?php
                        }
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="6">
                            <?php echo 'No movies found'; ?>
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>
</code>```

Solution 3 (recommended): PDO

The absolute winner.

```<code><?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');
define('CHARSET', 'utf8');

// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!

/*
 * Create a PDO instance as db connection to db.
 *
 * See: http://php.net/manual/en/class.pdo.php
 * See: http://php.net/manual/en/pdo.constants.php
 * See: http://php.net/manual/en/pdo.error-handling.php
 * See: http://php.net/manual/en/pdo.connections.php
 */
$connection = new PDO(
        sprintf('mysql:host=%s;port=%s;dbname=%s;charset=%s', HOST, PORT, DATABASE, CHARSET)
        , USERNAME
        , PASSWORD
        , [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => FALSE,
    PDO::ATTR_PERSISTENT => TRUE
        ]
);

// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;

/*
 * The SQL statement to be prepared. Notice the so-called named markers.
 * They will be replaced later with the corresponding values from the
 * bindings array when using PDOStatement::bindValue.
 *
 * When using named markers, the bindings array will be an associative
 * array, with the key names corresponding to the named markers from
 * the sql statement.
 *
 * You can also use question mark markers. In this case, the bindings
 * array will be an indexed array, with keys beginning from 1 (not 0).
 * Each array key corresponds to the position of the marker in the sql
 * statement.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$sql = sprintf(
        'SELECT
                mv.movie_id,
                mv.title,
                mv.rating,
                mv.Runtime,
                mv.movie_rating,
                mv.release_date,
                acin.actor_name
            FROM movies AS mv
            LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
            LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
            %s'
        , isset($movieId) ? 'WHERE mv.movie_id = :movie_id' : ''
);

/**
 * The bindings array, mapping the named markers from the sql
 * statement to the corresponding values. It will be directly
 * passed as argument to the PDOStatement::execute method.
 *
 * See: http://php.net/manual/en/pdostatement.execute.php
 */
$bindings = [];
if (isset($movieId)) {
    $bindings[':movie_id'] = $movieId;
}

/*
 * Prepare the sql statement for execution and return a statement object.
 *
 * See: http://php.net/manual/en/pdo.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Execute the prepared statement. Because the bindings array
 * is directly passed as argument, there is no need to use any
 * binding method for each sql statement's marker (like
 * PDOStatement::bindParam or PDOStatement::bindValue).
 *
 * See: http://php.net/manual/en/pdostatement.execute.php
 */
$executed = $statement->execute($bindings);

/*
 * Fetch data (all at once) and save it into $fetchedData array.
 *
 * See: http://php.net/manual/en/pdostatement.fetchall.php
 */
$fetchedData = $statement->fetchAll(PDO::FETCH_ASSOC);

// Just for testing. Display fetched data.
echo '```' . print_r($fetchedData, TRUE) . '```';

// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
    $movieId = $item['movie_id'];
    $title = $item['title'];
    $rating = $item['rating'];
    $runtime = $item['Runtime'];
    $movieRating = $item['movie_rating'];
    $releaseDate = $item['release_date'];
    $actorName = $item['actor_name'];

    if (!array_key_exists($movieId, $movies)) {
        $movies[$movieId] = [
            'title' => $title,
            'rating' => $rating,
            'Runtime' => $runtime,
            'movie_rating' => $movieRating,
            'release_date' => $releaseDate,
        ];
    }

    if (isset($actorName)) {
        $movies[$movieId]['actors'][] = $actorName;
    } else {
        $movies[$movieId]['actors'] = [];
    }
}

// Just for testing. Display movies list.
//echo '```' . print_r($movies, TRUE) . '```';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
            .movies { border-collapse: collapse; border: 1px solid #ccc; }
            .movies td, th { padding: 5px; }
            .movie-record { color: white; background-color: #00b3ee; }
            .actor-record { background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <h3>
            Movies list
        </h3>

        <table class="movies">
            <thead>
                <tr>
                    <th>Movie ID</th>
                    <th>Title</th>
                    <th>Rating</th>
                    <th>Runtime</th>
                    <th>Movie Rating</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($movies) {
                    foreach ($movies as $movieId => $movie) {
                        $title = $movie['title'];
                        $rating = $movie['rating'];
                        $runtime = $movie['Runtime'];
                        $movieRating = $movie['movie_rating'];
                        $releaseDate = $movie['release_date'];
                        ?>
                        <tr class="movie-record">
                            <td><?php echo $movieId; ?></td>
                            <td><?php echo $title; ?></td>
                            <td><?php echo $rating; ?></td>
                            <td><?php echo $runtime; ?></td>
                            <td><?php echo $movieRating; ?></td>
                            <td><?php echo $releaseDate; ?></td>
                        </tr>
                        <?php
                        foreach ($movie['actors'] as $actorName) {
                            ?>
                            <tr class="actor-record">
                                <td colspan="6"><?php echo $actorName; ?></td>
                            </tr>
                            <?php
                        }
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="6">
                            <?php echo 'No movies found'; ?>
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>
</code>```

Used data for testing:

```<code>Table movies:

movie_id    title               rating  Runtime movie_rating    release_date
----------------------------------------------------------------------------
1           Blade Runner 2049   R       164     8.5             2017-10-06
2           The Godfather       R       178     9.2             1972-04-24
3           Pulp Fiction        R       178     8.9             1994-10-14

Table actor_information:

actor_id    actor_name
--------------------------
1           Robert de Niro
2           Ryan Gosling
3           Ana de Armas
4           Marlon Brando
5           John Travolta
6           Julia Roberts

Table actor_movie:

movie_id    actor_id
--------------------
1           1
1           2
1           3
2           4
2           1
2           6
</code>```

Results:

Body:
I am trying to create a query, but I don't know how to select multiple tables with the way I have my database set up.

In my query I am trying to display what movies have what actor, but each movie has multiple actors so I have to select from three tables.
This is my movies table:

This is my actor_information table

My last table is the actor_movie table. This table correlates the movie_id with and actor_id

I have the following php to connect to the database, but I am not sure how to write the query portion.

<?php
$databaseName = 'movie_database';
$databaseUser = 'root';
$databasePassword = 'root';
$databaseHost = '127.0.0.1';

$conn = new mysqli($databaseHost, $databaseUser, $databasePassword, $databaseName);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

echo "Connected successfully
";

// this code is wrong, but i get the general idea of how to select from my db.
$sql = "SELECT actor_movie, movies, actor_information FROM actor_movie INNER
JOIN movies ON actor_movie.movie_id = movies.movie_id
INNER JOIN actor_information ON actor_movie.actor_id =
actor_information.actor_id"
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
    // output data of each row
    while($row = mysqli_fetch_assoc($result)) {
        echo " Title " . $row["title"]. "". $row["actor_id"]. "
";
    }
} else {
    echo "0 results";
}

mysqli_close($conn);
?>

Here is my uml if it helps:

Body:
As @tadman suggested, the object-oriented mysqli is better. My recommendation, though, is to use PDO instead of mysqli. Read this.

Read here and here to properly handle the errors/exceptions.

Note the use of the prepared statement (see here too) in order to avoid SQL injection.

You can also fetch just one movie.

The select uses LEFT JOINs: fetches all movies and "attaches" the actor details to each of them, by using actor_movie. There are also the movies fetched, that doesn't have any actors assigned yet (e.g. actor_name = NULL in result set).

After the result set is fetched, I built a 2nd array ($movies) to prepare the data to be properly displayed in the html table. Note that this step could be replaced by fetching data from db using multiple queries: one for fetching the movies list, and a query for each movie for fetching the actors.

Note the separation of data fetching code (php, upper page part) from the displaying code (html, lower page part). E.g: all data is fetched from db in arrays (php, upper page part) and only these arrays are then iterated in order to display the data (html, lower page part). E.g: no data fetching code is mixed with the data displaying code.

The db structure corresponds to the one kindly presented by you.

Notice the "use" of actor "Robert de Niro" (actor_id = 1) in two movies (see actor_movie table).

Good luck.

Note: all three solutions are identical, starting with the line: $movies = [];

Solution 1: mysqli using get_result() + fetch_all()

Works only if the mysqlnd driver ("MySQL Native Driver") is installed!

<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');

// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!

/**
 * Enable internal report functions. This enables the exception handling,
 * e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
 * (mysqli_sql_exception). They are catched in the try-catch block.
 *
 * MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
 * MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
 *
 * See: http://php.net/manual/en/class.mysqli-driver.php
 * See: http://php.net/manual/en/mysqli-driver.report-mode.php
 * See: http://php.net/manual/en/mysqli.constants.php
 */
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/**
 * Create a new db connection.
 *
 * @see http://php.net/manual/en/mysqli.construct.php
 */
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);

// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;

/*
 * The SQL statement to be prepared. Notice the so-called markers,
 * e.g. the "?" signs. They will be replaced later with the
 * corresponding values when using mysqli_stmt::bind_param.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$sql = sprintf(
        'SELECT
                mv.movie_id,
                mv.title,
                mv.rating,
                mv.Runtime,
                mv.movie_rating,
                mv.release_date,
                acin.actor_name
            FROM movies AS mv
            LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
            LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
            %s'
        , isset($movieId) ? 'WHERE mv.movie_id = ?' : ''
);

/*
 * Prepare the SQL statement for execution - ONLY ONCE.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Bind variables for the parameter markers (?) in the
 * SQL statement that was passed to prepare(). The first
 * argument of bind_param() is a string that contains one
 * or more characters which specify the types for the
 * corresponding bind variables.
 *
 * See: http://php.net/manual/en/mysqli-stmt.bind-param.php
 */
if (isset($movieId)) {
    $statement->bind_param('i', $movieId);
}

/*
 * Execute the prepared SQL statement.
 * When executed any parameter markers which exist will
 * automatically be replaced with the appropriate data.
 *
 * See: http://php.net/manual/en/mysqli-stmt.execute.php
 */
$executed = $statement->execute();

/*
 * Get the result set from the prepared statement.
 *
 * NOTA BENE:
 * Available only with mysqlnd ("MySQL Native Driver")! If this
 * is not installed, then uncomment "extension=php_mysqli_mysqlnd.dll" in
 * PHP config file (php.ini) and restart web server (I assume Apache) and
 * mysql service. Or use the following functions instead:
 * mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
 *
 * See:
 *      http://php.net/manual/en/mysqli-stmt.get-result.php
 *      https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$result = $statement->get_result();

/*
 * Fetch data and save it into $fetchedData array.
 *
 * See: http://php.net/manual/en/mysqli-result.fetch-all.php
 */
// Fetch all rows at once...
$fetchedData = $result->fetch_all(MYSQLI_ASSOC);

// ... OR fetch one row at a time.
// while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
//     $fetchedData[] = $row;
// }
//
// Just for testing. Display fetched data.
//echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';

/*
 * Free the memory associated with the result. You should
 * always free your result when it is not needed anymore.
 *
 * See: http://php.net/manual/en/mysqli-result.free.php
 */
$result->close();

/*
 * Close the prepared statement. It also deallocates the statement handle.
 * If the statement has pending or unread results, it cancels them
 * so that the next query can be executed.
 *
 * See: http://php.net/manual/en/mysqli-stmt.close.php
 */
$statementClosed = $statement->close();

// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
    $movieId = $item['movie_id'];
    $title = $item['title'];
    $rating = $item['rating'];
    $runtime = $item['Runtime'];
    $movieRating = $item['movie_rating'];
    $releaseDate = $item['release_date'];
    $actorName = $item['actor_name'];

    if (!array_key_exists($movieId, $movies)) {
        $movies[$movieId] = [
            'title' => $title,
            'rating' => $rating,
            'Runtime' => $runtime,
            'movie_rating' => $movieRating,
            'release_date' => $releaseDate,
        ];
    }

    if (isset($actorName)) {
        $movies[$movieId]['actors'][] = $actorName;
    } else {
        $movies[$movieId]['actors'] = [];
    }
}

// Just for testing. Display movies list.
//echo '<pre>' . print_r($movies, TRUE) . '</pre>';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
            .movies { border-collapse: collapse; border: 1px solid #ccc; }
            .movies td, th { padding: 5px; }
            .movie-record { color: white; background-color: #00b3ee; }
            .actor-record { background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <h3>
            Movies list
        </h3>

        <table class="movies">
            <thead>
                <tr>
                    <th>Movie ID</th>
                    <th>Title</th>
                    <th>Rating</th>
                    <th>Runtime</th>
                    <th>Movie Rating</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($movies) {
                    foreach ($movies as $movieId => $movie) {
                        $title = $movie['title'];
                        $rating = $movie['rating'];
                        $runtime = $movie['Runtime'];
                        $movieRating = $movie['movie_rating'];
                        $releaseDate = $movie['release_date'];
                        ?>
                        <tr class="movie-record">
                            <td><?php echo $movieId; ?></td>
                            <td><?php echo $title; ?></td>
                            <td><?php echo $rating; ?></td>
                            <td><?php echo $runtime; ?></td>
                            <td><?php echo $movieRating; ?></td>
                            <td><?php echo $releaseDate; ?></td>
                        </tr>
                        <?php
                        foreach ($movie['actors'] as $actorName) {
                            ?>
                            <tr class="actor-record">
                                <td colspan="6"><?php echo $actorName; ?></td>
                            </tr>
                            <?php
                        }
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="6">
                            <?php echo 'No movies found'; ?>
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>

Solution 2: mysqli using store_result() + bind_result() + fetch()

Hard to work with. But it works even if the mysqlnd driver ("MySQL Native Driver") is not installed.

<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');

// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!

/**
 * Enable internal report functions. This enables the exception handling,
 * e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
 * (mysqli_sql_exception). They are catched in the try-catch block.
 *
 * MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
 * MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
 *
 * See: http://php.net/manual/en/class.mysqli-driver.php
 * See: http://php.net/manual/en/mysqli-driver.report-mode.php
 * See: http://php.net/manual/en/mysqli.constants.php
 */
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/**
 * Create a new db connection.
 *
 * @see http://php.net/manual/en/mysqli.construct.php
 */
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);

// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;

/*
 * The SQL statement to be prepared. Notice the so-called markers,
 * e.g. the "?" signs. They will be replaced later with the
 * corresponding values when using mysqli_stmt::bind_param.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$sql = sprintf(
        'SELECT
                mv.movie_id,
                mv.title,
                mv.rating,
                mv.Runtime,
                mv.movie_rating,
                mv.release_date,
                acin.actor_name
            FROM movies AS mv
            LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
            LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
            %s'
        , isset($movieId) ? 'WHERE mv.movie_id = ?' : ''
);

/*
 * Prepare the SQL statement for execution - ONLY ONCE.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Bind variables for the parameter markers (?) in the
 * SQL statement that was passed to prepare(). The first
 * argument of bind_param() is a string that contains one
 * or more characters which specify the types for the
 * corresponding bind variables.
 *
 * See: http://php.net/manual/en/mysqli-stmt.bind-param.php
 */
if (isset($movieId)) {
    $statement->bind_param('i', $movieId);
}

/*
 * Execute the prepared SQL statement.
 * When executed any parameter markers which exist will
 * automatically be replaced with the appropriate data.
 *
 * See: http://php.net/manual/en/mysqli-stmt.execute.php
 */
$executed = $statement->execute();

/*
 * Transfer the result set resulted from executing the prepared statement.
 * E.g. store, e.g. buffer the result set into the (same) prepared statement.
 *
 * See:
 *      http://php.net/manual/en/mysqli-stmt.store-result.php
 *      https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$resultStored = $statement->store_result();

/*
 * Bind the result set columns to corresponding variables.
 * E.g. these variables will hold the column values after fetching.
 *
 * See: http://php.net/manual/en/mysqli-stmt.bind-result.php
 */
$varsBound = $statement->bind_result(
        $boundMovieId
        , $boundTitle
        , $boundRating
        , $boundRuntime
        , $boundMovieRating
        , $boundReleaseDate
        , $boundActorName
);

/*
 * Fetch results from the result set (of the prepared statement) into the bound variables.
 *
 * See: http://php.net/manual/en/mysqli-stmt.fetch.php
 */
$fetchedData = [];
while ($row = $statement->fetch()) {
    $fetchedData[] = [
        'movie_id' => $boundMovieId,
        'title' => $boundTitle,
        'rating' => $boundRating,
        'Runtime' => $boundRuntime,
        'movie_rating' => $boundMovieRating,
        'release_date' => $boundReleaseDate,
        'actor_name' => $boundActorName,
    ];
}

// Just for testing. Display fetched data.
//echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';

/*
 * Frees the result memory associated with the statement,
 * which was allocated by mysqli_stmt::store_result.
 *
 * See: http://php.net/manual/en/mysqli-stmt.store-result.php
 */
$statement->free_result();

/*
 * Close the prepared statement. It also deallocates the statement handle.
 * If the statement has pending or unread results, it cancels them
 * so that the next query can be executed.
 *
 * See: http://php.net/manual/en/mysqli-stmt.close.php
 */
$statementClosed = $statement->close();

// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
    $movieId = $item['movie_id'];
    $title = $item['title'];
    $rating = $item['rating'];
    $runtime = $item['Runtime'];
    $movieRating = $item['movie_rating'];
    $releaseDate = $item['release_date'];
    $actorName = $item['actor_name'];

    if (!array_key_exists($movieId, $movies)) {
        $movies[$movieId] = [
            'title' => $title,
            'rating' => $rating,
            'Runtime' => $runtime,
            'movie_rating' => $movieRating,
            'release_date' => $releaseDate,
        ];
    }

    if (isset($actorName)) {
        $movies[$movieId]['actors'][] = $actorName;
    } else {
        $movies[$movieId]['actors'] = [];
    }
}

// Just for testing. Display movies list.
//echo '<pre>' . print_r($movies, TRUE) . '</pre>';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
            .movies { border-collapse: collapse; border: 1px solid #ccc; }
            .movies td, th { padding: 5px; }
            .movie-record { color: white; background-color: #00b3ee; }
            .actor-record { background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <h3>
            Movies list
        </h3>

        <table class="movies">
            <thead>
                <tr>
                    <th>Movie ID</th>
                    <th>Title</th>
                    <th>Rating</th>
                    <th>Runtime</th>
                    <th>Movie Rating</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($movies) {
                    foreach ($movies as $movieId => $movie) {
                        $title = $movie['title'];
                        $rating = $movie['rating'];
                        $runtime = $movie['Runtime'];
                        $movieRating = $movie['movie_rating'];
                        $releaseDate = $movie['release_date'];
                        ?>
                        <tr class="movie-record">
                            <td><?php echo $movieId; ?></td>
                            <td><?php echo $title; ?></td>
                            <td><?php echo $rating; ?></td>
                            <td><?php echo $runtime; ?></td>
                            <td><?php echo $movieRating; ?></td>
                            <td><?php echo $releaseDate; ?></td>
                        </tr>
                        <?php
                        foreach ($movie['actors'] as $actorName) {
                            ?>
                            <tr class="actor-record">
                                <td colspan="6"><?php echo $actorName; ?></td>
                            </tr>
                            <?php
                        }
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="6">
                            <?php echo 'No movies found'; ?>
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>

Solution 3 (recommended): PDO

The absolute winner.

<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');
define('CHARSET', 'utf8');

// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!

/*
 * Create a PDO instance as db connection to db.
 *
 * See: http://php.net/manual/en/class.pdo.php
 * See: http://php.net/manual/en/pdo.constants.php
 * See: http://php.net/manual/en/pdo.error-handling.php
 * See: http://php.net/manual/en/pdo.connections.php
 */
$connection = new PDO(
        sprintf('mysql:host=%s;port=%s;dbname=%s;charset=%s', HOST, PORT, DATABASE, CHARSET)
        , USERNAME
        , PASSWORD
        , [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => FALSE,
    PDO::ATTR_PERSISTENT => TRUE
        ]
);

// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;

/*
 * The SQL statement to be prepared. Notice the so-called named markers.
 * They will be replaced later with the corresponding values from the
 * bindings array when using PDOStatement::bindValue.
 *
 * When using named markers, the bindings array will be an associative
 * array, with the key names corresponding to the named markers from
 * the sql statement.
 *
 * You can also use question mark markers. In this case, the bindings
 * array will be an indexed array, with keys beginning from 1 (not 0).
 * Each array key corresponds to the position of the marker in the sql
 * statement.
 *
 * See: http://php.net/manual/en/mysqli.prepare.php
 */
$sql = sprintf(
        'SELECT
                mv.movie_id,
                mv.title,
                mv.rating,
                mv.Runtime,
                mv.movie_rating,
                mv.release_date,
                acin.actor_name
            FROM movies AS mv
            LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
            LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
            %s'
        , isset($movieId) ? 'WHERE mv.movie_id = :movie_id' : ''
);

/**
 * The bindings array, mapping the named markers from the sql
 * statement to the corresponding values. It will be directly
 * passed as argument to the PDOStatement::execute method.
 *
 * See: http://php.net/manual/en/pdostatement.execute.php
 */
$bindings = [];
if (isset($movieId)) {
    $bindings[':movie_id'] = $movieId;
}

/*
 * Prepare the sql statement for execution and return a statement object.
 *
 * See: http://php.net/manual/en/pdo.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Execute the prepared statement. Because the bindings array
 * is directly passed as argument, there is no need to use any
 * binding method for each sql statement's marker (like
 * PDOStatement::bindParam or PDOStatement::bindValue).
 *
 * See: http://php.net/manual/en/pdostatement.execute.php
 */
$executed = $statement->execute($bindings);

/*
 * Fetch data (all at once) and save it into $fetchedData array.
 *
 * See: http://php.net/manual/en/pdostatement.fetchall.php
 */
$fetchedData = $statement->fetchAll(PDO::FETCH_ASSOC);

// Just for testing. Display fetched data.
echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';

// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
    $movieId = $item['movie_id'];
    $title = $item['title'];
    $rating = $item['rating'];
    $runtime = $item['Runtime'];
    $movieRating = $item['movie_rating'];
    $releaseDate = $item['release_date'];
    $actorName = $item['actor_name'];

    if (!array_key_exists($movieId, $movies)) {
        $movies[$movieId] = [
            'title' => $title,
            'rating' => $rating,
            'Runtime' => $runtime,
            'movie_rating' => $movieRating,
            'release_date' => $releaseDate,
        ];
    }

    if (isset($actorName)) {
        $movies[$movieId]['actors'][] = $actorName;
    } else {
        $movies[$movieId]['actors'] = [];
    }
}

// Just for testing. Display movies list.
//echo '<pre>' . print_r($movies, TRUE) . '</pre>';
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
            .movies { border-collapse: collapse; border: 1px solid #ccc; }
            .movies td, th { padding: 5px; }
            .movie-record { color: white; background-color: #00b3ee; }
            .actor-record { background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <h3>
            Movies list
        </h3>

        <table class="movies">
            <thead>
                <tr>
                    <th>Movie ID</th>
                    <th>Title</th>
                    <th>Rating</th>
                    <th>Runtime</th>
                    <th>Movie Rating</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($movies) {
                    foreach ($movies as $movieId => $movie) {
                        $title = $movie['title'];
                        $rating = $movie['rating'];
                        $runtime = $movie['Runtime'];
                        $movieRating = $movie['movie_rating'];
                        $releaseDate = $movie['release_date'];
                        ?>
                        <tr class="movie-record">
                            <td><?php echo $movieId; ?></td>
                            <td><?php echo $title; ?></td>
                            <td><?php echo $rating; ?></td>
                            <td><?php echo $runtime; ?></td>
                            <td><?php echo $movieRating; ?></td>
                            <td><?php echo $releaseDate; ?></td>
                        </tr>
                        <?php
                        foreach ($movie['actors'] as $actorName) {
                            ?>
                            <tr class="actor-record">
                                <td colspan="6"><?php echo $actorName; ?></td>
                            </tr>
                            <?php
                        }
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="6">
                            <?php echo 'No movies found'; ?>
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>

Used data for testing:

Table movies:

movie_id    title               rating  Runtime movie_rating    release_date
----------------------------------------------------------------------------
1           Blade Runner 2049   R       164     8.5             2017-10-06
2           The Godfather       R       178     9.2             1972-04-24
3           Pulp Fiction        R       178     8.9             1994-10-14

Table actor_information:

actor_id    actor_name
--------------------------
1           Robert de Niro
2           Ryan Gosling
3           Ana de Armas
4           Marlon Brando
5           John Travolta
6           Julia Roberts

Table actor_movie:

movie_id    actor_id
--------------------
1           1
1           2
1           3
2           4
2           1
2           6

Results:


来源: stackexchange

问:
Strange behavior with function overriding and optional params in Groovy
The behavior is best explained with a little code. In this example calling super.doSomething(t) from class y without the optional parameter results in it recursively calling itself (y.doSomething). Is this a bug or is there a explanation why leaving off the optional param and calling super results in a recursive call.

You can try it out here: https://tio.run/#groovy

```<code>class x {
    void doSomething(Integer x, Boolean n=false){
        println("Super ${n}")
    }
}

class y extends x {
    @Override
    void doSomething(Integer t, Boolean q=true){
        println("Sub")
        super.doSomething(t)  //Stack overflow - super.doSomething calls the subclass
        super.doSomething(t, q)  //Works as expected
    }
}

new y().doSomething(4)
</code>```

答:
The code below is basically what gets generated for what you have and this representation should help make sense of it:</p>

```<code>class X {
    void doSomething(Integer x) {
        // remember that for an instance of Y, this is
        // going to call doSomething(Integer, Boolean) in
        // the subclass (Y), not this class (X).
        doSomething x, false
    }

    void doSomething(Integer x, Boolean n) {
        println("Super ${n}")
    }
}

class Y extends X {
    void doSomething(Integer t) {
        doSomething t, true
    }
    @Override
    void doSomething(Integer t, Boolean q){
        println("Sub")

        // stack overflow because this calls doSomething(Integer) in
        // the parent class which is going to call
        // doSomething(Integer, Boolean), which will end up right back here
        super.doSomething(t)

        // no stack overflow
        super.doSomething(t, q)
    }
}
</code>```

ContentLicense: CC BY-SA 3.0

Body:
The behavior is best explained with a little code. In this example calling super.doSomething(t) from class y without the optional parameter results in it recursively calling itself (y.doSomething). Is this a bug or is there a explanation why leaving off the optional param and calling super results in a recursive call.

You can try it out here: https://tio.run/#groovy

class x {
    void doSomething(Integer x, Boolean n=false){
        println("Super ${n}")
    }
}

class y extends x {
    @Override
    void doSomething(Integer t, Boolean q=true){
        println("Sub")
        super.doSomething(t)  //Stack overflow - super.doSomething calls the subclass
        super.doSomething(t, q)  //Works as expected
    }
}

new y().doSomething(4)

Body:
The code below is basically what gets generated for what you have and this representation should help make sense of it:

class X {
    void doSomething(Integer x) {
        // remember that for an instance of Y, this is
        // going to call doSomething(Integer, Boolean) in
        // the subclass (Y), not this class (X).
        doSomething x, false
    }

    void doSomething(Integer x, Boolean n) {
        println("Super ${n}")
    }
}

class Y extends X {
    void doSomething(Integer t) {
        doSomething t, true
    }
    @Override
    void doSomething(Integer t, Boolean q){
        println("Sub")

        // stack overflow because this calls doSomething(Integer) in
        // the parent class which is going to call
        // doSomething(Integer, Boolean), which will end up right back here
        super.doSomething(t)

        // no stack overflow
        super.doSomething(t, q)
    }
}


问:
Correct way to use Actions to create menus, toolbars, and other components in Java
The naive way of writing building a menu in a Java Swing app is to do something like:

```<code>JMenu fileMenu = new JMenu("File");
JMenuItem openItem = new JMenuItem("Open...");
openItem.addActionListener(new ActionListener() { /* action listener stuff */ } )
fileMenu.addMenuItem(openItem);
</code>```

A more experienced developer will recognize that actions can be accessed through a variety of mechanisms - menus, toolbar buttons, maybe even other workflows in the system.  That person is more likely to write:

```<code>Action openAction = new AbstractAction();
openAction.setName("Open...");
openAction.addActionListener(new ActionListener() { /* action listener stuff */ } )
...
JMenuItem openItem = new JMenuItem(openAction);
</code>```

My question is, what is the best way to manage these Action objects so they can be used across menus, toolbars, etc?

Create a factory class that returns specific actions?

Declare all of the actions as private static final Action in some utility class?

Take advantage of a Java application framework?

Something else?

答:
You can group all your abstractAction using the dedicated Map javax.swing.actionmap .
See http://java.sun.com/javase/6/docs/api/javax/swing/ActionMap.html

Moreover each JComponent has an internal actionMap (getActionMap()).

```<code>class MyComponent
extends JPanel
{
public static final String ACTION_NAME1="my.action.1";

public MyComponent()
 {
 AbstractAction action= new AbstractAction() { ... }
 getActionMap().put(ACTION_NAME1,action);
...

 menu.add(getActionMap().get(ACTION_NAME1));
 }

}
</code>```

Hope it helps

OwnerDisplayName: David

ContentLicense: CC BY-SA 2.5

Body:
The naive way of writing building a menu in a Java Swing app is to do something like:

JMenu fileMenu = new JMenu("File");
JMenuItem openItem = new JMenuItem("Open...");
openItem.addActionListener(new ActionListener() { /* action listener stuff */ } )
fileMenu.addMenuItem(openItem);

A more experienced developer will recognize that actions can be accessed through a variety of mechanisms - menus, toolbar buttons, maybe even other workflows in the system.  That person is more likely to write:

Action openAction = new AbstractAction();
openAction.setName("Open...");
openAction.addActionListener(new ActionListener() { /* action listener stuff */ } )
...
JMenuItem openItem = new JMenuItem(openAction);

My question is, what is the best way to manage these Action objects so they can be used across menus, toolbars, etc?

Create a factory class that returns specific actions?

Declare all of the actions as private static final Action in some utility class?

Take advantage of a Java application framework?

Something else?

LastEditorDisplayName: David

Body:
You can group all your abstractAction using the dedicated Map javax.swing.actionmap .
See http://java.sun.com/javase/6/docs/api/javax/swing/ActionMap.html

Moreover each JComponent has an internal actionMap (getActionMap()).

class MyComponent
extends JPanel
{
public static final String ACTION_NAME1="my.action.1";

public MyComponent()
 {
 AbstractAction action= new AbstractAction() { ... }
 getActionMap().put(ACTION_NAME1,action);
...

 menu.add(getActionMap().get(ACTION_NAME1));
 }

}

Hope it helps


问:
Add multiple choice score
I need to add up the score in a multiple choice quiz.

This is my code:

```<code>from tkinter import *
import time, os, random

global questions

questions = []

questions = ['''
What does ? equal to:

43+(5x(6)**8)/8-8+8 = ?

1) 10356
2) 1049803
3) 202367
4) 12742130
5) 343321

''',
'''
Find the range, the median, the mode and the mean:

>>> 232, 4343, 434, 232.4, 43, 5453, 434, 232, 645, 23.3, 53453.3, 434 <<<

1) 53410.3, 2943.5, 434, 5496.58
2) 53410.3, 5956, 434, 5453.28
3) 232, 43, 3452, 421, 642
4) 232, 43, 43, 434
5) 232, 5453, 645, 53453.3

''',
'''
In 2004, the minimum wage in Ontario was $7.15.
In 2008, it was $8.15. What is the rate of change?

1) $0.90
2) $3.98
3) $1.54
4) $0.40
5) $1.80

''',
'''
Find the value of n:

--------------------------------------

    43n/2n  x (99.99/9)x7 = 99.99n

--------------------------------------

1) n = 99.92
2) n = 12.43
3) n = 16.73
4) n = 104.4
5) n = 3.98

''',
'''
Which one is NOT a linear equation?

1) y = 54x*3
2) y = 23x*7
3) y = -x
4) All of the Above
5) None of the Above

''',
'''
What is the formula for finding the surface area for a sphere?

1) 4/3πr**3
2) πr**2
3) 2πrh + 2πr**2
4) πd
5) 4πr**2

''',
'''
Which variable represents the slope?
y = mx + c

1) y
2) m
3) x
4) c
5) None of the above
''',
'''
An isosceles triangle has the top angle of 6x
and a supplementary angle of a base angle is 21x. What are the
measures of the angles of isosceles triangle?

1) 22.5, 78.75, 78.75
2) 108.97, 35.52, 35.52
3) 76.8, 51.6, 51.6
4) 20, 20, 140
5) 43.6, 5.5, 98.53

''',
'''
A sphere fits inside a rectangular box of 6cm by 6cm by 7cm.
What is the greatest possible volume of the sphere?

1) 113.04 cm*2
2) 110.46 cm*3
3) 113.04 cm*3
4) 110.46 cm*2
5) 142.9 cm*3

''',
'''
If the area of an isosceles triangle is 196 cm**2
and the height is 19.6 cm, what would it's perimeter be?

1) 53.9 cm
2) 64 cm
3) 76 cm
4) 32.43 cm
5) 32.32 cm

'''
]
global answers
global picked
global userans
global ansRandOrder
global score

answers = ['2', '1', '4', '3', '5', '5', '2', '1', '3', '2']
picked = []
userans = []
ansRandOrder = []
score = 0

def nexT():
    global userans
    userans.append(e1.get())
    print(userans)
    print(ansRandOrder)
    global score
    global i
    i = 0
    if userans[i] == ansRandOrder[i]:
        score += 1
    i += 1
    print(score)
    self.destroy()
    ques()

def ques():
    global self
    global e1
    global ansRandOrder
    global picked

    self = Tk()

    w = 500
    h = 375

    ws = self.winfo_screenwidth()
    hs = self.winfo_screenheight()

    x = (ws/2) - (w/2)
    y = (hs/2) - (h/2)

    self.geometry('%dx%d+%d+%d' % (w, h, x, y))

    x = random.randint(0, 9)
    while x in picked:
        x = random.randint(0, 9)

    picked.append(x)

    ansRandOrder.append(answers[x])

    Label(self, text = '\n').pack()

    Label(self, text = questions[x]).pack()

    e1 = Entry(self)

    e1.pack()

    Label(self, text = '\n').pack()

    nex = Button(self, text = 'Next Question', command = nexT).pack()

    self.mainloop()

ques()
</code>```

The result is always score = 0.

How can I correctly add the score?

答:
I've experimented a bit with your code and the issue seems to lie elsewhere. For some reason or another the script always compares the wrong answers for the if in nexT. Other times, after restarting the script without clearing old variables, it always thinks the answer is correct.

I think this issue will probably go away if you clean up your code a bit. I'd recommend you rewrite your code as a class, which is a common approach to Tkinter scripts. The main advantages for you would be:

It's a lot more readable (It'll be easier to debug)

No more global variables, instead you just refer to the variables as part of the class (self.variable)

When the program exits (which it doesn't seem to do properly at the moment) you can just finish with something like del MyClass to get rid of the variable clutter. That clutter is probably why the behaviour of this bug changes from time to time.

Have a look at this thread for some good examples and explanations about building a tkinter class-based application:
Best way to structure a tkinter application

As an example for your quiz, it might look something like this: (Non-functional)

```<code>class Quiz():

    def __init__(self):

        self.questions = []
        self.WriteQuestions()

        self.answers = ['2', '1', '4', '3', '5', '5', '2', '1', '3', '2']
        self.picked = []
        self.userans = []
        self.ansRandOrder = []
        self.score = 0
        #"self." instead of "global"
        # Any def inside this class can refer to self."variable" and use it or update it.

        self.Question()

    def WriteQuestions(self):
        # All your questions here, just so they're out of the way.

    def NextQuestion(self):

        self.userans.append(e1.get())
        self.i = 0
        if self.userans[i] == ansRandOrder[i]:
            self.score += 1
        i += 1
        print(self.score)
        root.destroy() # You'll need to pick a name other than "self" for your tk window
        self.Question()
        # self is a reserved keyword for classes and shouldn't be used for anything other
        # than telling a class that you're referring to one of its own variables or functions.

    def Question(self):

        # etc...

if __name__ == 'main':

    # set up tkloop here
    # create an instance of the Quiz class, e.g.
    Qz = Quiz()

    # finally, after the loop finishes:

    del Qz # To get rid of the old variables
</code>```

Title: Add multiple choice score

Body:
I need to add up the score in a multiple choice quiz.

This is my code:

from tkinter import *
import time, os, random

global questions

questions = []

questions = ['''
What does ? equal to:

43+(5x(6)**8)/8-8+8 = ?

1) 10356
2) 1049803
3) 202367
4) 12742130
5) 343321

''',
'''
Find the range, the median, the mode and the mean:

>>> 232, 4343, 434, 232.4, 43, 5453, 434, 232, 645, 23.3, 53453.3, 434 <<<

1) 53410.3, 2943.5, 434, 5496.58
2) 53410.3, 5956, 434, 5453.28
3) 232, 43, 3452, 421, 642
4) 232, 43, 43, 434
5) 232, 5453, 645, 53453.3

''',
'''
In 2004, the minimum wage in Ontario was $7.15.
In 2008, it was $8.15. What is the rate of change?

1) $0.90
2) $3.98
3) $1.54
4) $0.40
5) $1.80

''',
'''
Find the value of n:

--------------------------------------

    43n/2n  x (99.99/9)x7 = 99.99n

--------------------------------------

1) n = 99.92
2) n = 12.43
3) n = 16.73
4) n = 104.4
5) n = 3.98

''',
'''
Which one is NOT a linear equation?

1) y = 54x*3
2) y = 23x*7
3) y = -x
4) All of the Above
5) None of the Above

''',
'''
What is the formula for finding the surface area for a sphere?

1) 4/3πr**3
2) πr**2
3) 2πrh + 2πr**2
4) πd
5) 4πr**2

''',
'''
Which variable represents the slope?
y = mx + c

1) y
2) m
3) x
4) c
5) None of the above
''',
'''
An isosceles triangle has the top angle of 6x
and a supplementary angle of a base angle is 21x. What are the
measures of the angles of isosceles triangle?

1) 22.5, 78.75, 78.75
2) 108.97, 35.52, 35.52
3) 76.8, 51.6, 51.6
4) 20, 20, 140
5) 43.6, 5.5, 98.53

''',
'''
A sphere fits inside a rectangular box of 6cm by 6cm by 7cm.
What is the greatest possible volume of the sphere?

1) 113.04 cm*2
2) 110.46 cm*3
3) 113.04 cm*3
4) 110.46 cm*2
5) 142.9 cm*3

''',
'''
If the area of an isosceles triangle is 196 cm**2
and the height is 19.6 cm, what would it's perimeter be?

1) 53.9 cm
2) 64 cm
3) 76 cm
4) 32.43 cm
5) 32.32 cm

'''
]
global answers
global picked
global userans
global ansRandOrder
global score

answers = ['2', '1', '4', '3', '5', '5', '2', '1', '3', '2']
picked = []
userans = []
ansRandOrder = []
score = 0

def nexT():
    global userans
    userans.append(e1.get())
    print(userans)
    print(ansRandOrder)
    global score
    global i
    i = 0
    if userans[i] == ansRandOrder[i]:
        score += 1
    i += 1
    print(score)
    self.destroy()
    ques()

def ques():
    global self
    global e1
    global ansRandOrder
    global picked

    self = Tk()

    w = 500
    h = 375

    ws = self.winfo_screenwidth()
    hs = self.winfo_screenheight()

    x = (ws/2) - (w/2)
    y = (hs/2) - (h/2)

    self.geometry('%dx%d+%d+%d' % (w, h, x, y))

    x = random.randint(0, 9)
    while x in picked:
        x = random.randint(0, 9)

    picked.append(x)

    ansRandOrder.append(answers[x])

    Label(self, text = '\n').pack()

    Label(self, text = questions[x]).pack()

    e1 = Entry(self)

    e1.pack()

    Label(self, text = '\n').pack()

    nex = Button(self, text = 'Next Question', command = nexT).pack()

    self.mainloop()

ques()

The result is always score = 0.

How can I correctly add the score?

Body:
I've experimented a bit with your code and the issue seems to lie elsewhere. For some reason or another the script always compares the wrong answers for the if in nexT. Other times, after restarting the script without clearing old variables, it always thinks the answer is correct.

I think this issue will probably go away if you clean up your code a bit. I'd recommend you rewrite your code as a class, which is a common approach to Tkinter scripts. The main advantages for you would be:

It's a lot more readable (It'll be easier to debug)

No more global variables, instead you just refer to the variables as part of the class (self.variable)

When the program exits (which it doesn't seem to do properly at the moment) you can just finish with something like del MyClass to get rid of the variable clutter. That clutter is probably why the behaviour of this bug changes from time to time.

Have a look at this thread for some good examples and explanations about building a tkinter class-based application:
Best way to structure a tkinter application

As an example for your quiz, it might look something like this: (Non-functional)

class Quiz():

    def __init__(self):

        self.questions = []
        self.WriteQuestions()

        self.answers = ['2', '1', '4', '3', '5', '5', '2', '1', '3', '2']
        self.picked = []
        self.userans = []
        self.ansRandOrder = []
        self.score = 0
        #"self." instead of "global"
        # Any def inside this class can refer to self."variable" and use it or update it.

        self.Question()

    def WriteQuestions(self):
        # All your questions here, just so they're out of the way.

    def NextQuestion(self):

        self.userans.append(e1.get())
        self.i = 0
        if self.userans[i] == ansRandOrder[i]:
            self.score += 1
        i += 1
        print(self.score)
        root.destroy() # You'll need to pick a name other than "self" for your tk window
        self.Question()
        # self is a reserved keyword for classes and shouldn't be used for anything other
        # than telling a class that you're referring to one of its own variables or functions.

    def Question(self):

        # etc...

if __name__ == 'main':

    # set up tkloop here
    # create an instance of the Quiz class, e.g.
    Qz = Quiz()

    # finally, after the loop finishes:

    del Qz # To get rid of the old variables


问:
Cannot remove an image inserted with MediaStore
I'm saving an image loaded with Picasso to be shared afterwards. This is the code I'm using to save it:

```<code>String path = MediaStore.Images.Media.insertImage(
                    mContext.getContentResolver(), mImageBitmap, "Shared image", null);

return Uri.parse(path);
</code>```

Now, when the image has been shared, I have to delete it using the URI. I've tried some answers I've seen here but none of them has worked. The image still appears in the gallery. These are the methods I have used:

```<code>// Set up the projection (we only need the ID)
String[] projection = { MediaStore.Images.Media._ID };

// Match on the file path
String selection = MediaStore.Images.Media.DATA + " = ?";
String[] selectionArgs = new String[] { new File(mImageUri.toString()).getAbsolutePath() };

// Query for the ID of the media matching the file path
Uri queryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = getContentResolver();
Cursor c = contentResolver.query(queryUri, projection, selection, selectionArgs, null);

if (c.moveToFirst())
{
    // We found the ID. Deleting the item via the content provider will also remove the file
    long id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
    Uri deleteUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
    contentResolver.delete(deleteUri, null, null);
}

c.close();
</code>```

```<code>File file = new File(mImageUri.toString());
// This returns false
file.delete();

context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(mImageUri.toString()))));
</code>```

Any idea of what i've done wrong?

Thanks,

答:
Try this for deletion

```<code>    String[] retCol = { MediaStore.Audio.Media._ID };

        Cursor cur = context.getContentResolver().query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                retCol,
                MediaStore.Images.Media.TITLE + "='"+TEMP_FILE_TITLE+"'", null, null
        );
    try {
        if (cur.getCount() == 0) {
            return;
        }
        cur.moveToFirst();
        int id = cur.getInt(cur.getColumnIndex(MediaStore.MediaColumns._ID));
        Uri uri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id
        );
       int cnt = context.getContentResolver().delete(uri, null, null);
}finally {
                cur.close();
            }
</code>```

In your case replace TEMP_FILE_TITLE = "Shared image"

Body:
I'm saving an image loaded with Picasso to be shared afterwards. This is the code I'm using to save it:

String path = MediaStore.Images.Media.insertImage(
                    mContext.getContentResolver(), mImageBitmap, "Shared image", null);

return Uri.parse(path);

Now, when the image has been shared, I have to delete it using the URI. I've tried some answers I've seen here but none of them has worked. The image still appears in the gallery. These are the methods I have used:

// Set up the projection (we only need the ID)
String[] projection = { MediaStore.Images.Media._ID };

// Match on the file path
String selection = MediaStore.Images.Media.DATA + " = ?";
String[] selectionArgs = new String[] { new File(mImageUri.toString()).getAbsolutePath() };

// Query for the ID of the media matching the file path
Uri queryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = getContentResolver();
Cursor c = contentResolver.query(queryUri, projection, selection, selectionArgs, null);

if (c.moveToFirst())
{
    // We found the ID. Deleting the item via the content provider will also remove the file
    long id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
    Uri deleteUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
    contentResolver.delete(deleteUri, null, null);
}

c.close();

File file = new File(mImageUri.toString());
// This returns false
file.delete();

context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(mImageUri.toString()))));

Any idea of what i've done wrong?

Thanks,

Body:
Try this for deletion

    String[] retCol = { MediaStore.Audio.Media._ID };

        Cursor cur = context.getContentResolver().query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                retCol,
                MediaStore.Images.Media.TITLE + "='"+TEMP_FILE_TITLE+"'", null, null
        );
    try {
        if (cur.getCount() == 0) {
            return;
        }
        cur.moveToFirst();
        int id = cur.getInt(cur.getColumnIndex(MediaStore.MediaColumns._ID));
        Uri uri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id
        );
       int cnt = context.getContentResolver().delete(uri, null, null);
}finally {
                cur.close();
            }

In your case replace TEMP_FILE_TITLE = "Shared image"


问:
Apache POI xls column Remove
I don't find how to remove a column with the Apache POI API.

I would appreciate a sample code or help on this point.

答:
codewing's solution worked for me like a charm with the following minor changes:

When we clone the cell, the call should be cloneCell(cNew, cNext)

We should set the column width only for the first row.

I'm using version 3.17 of the api, so a few things changed (like CellType changed from int to an enum).

Full code is below (for clarity):

```<code>private void deleteColumn(Sheet sheet, int columnToDelete) {
    for (int rId = 0; rId < sheet.getLastRowNum(); rId++) {
        Row row = sheet.getRow(rId);
        for (int cID = columnToDelete; cID < row.getLastCellNum(); cID++) {
            Cell cOld = row.getCell(cID);
            if (cOld != null) {
                row.removeCell(cOld);
            }
            Cell cNext = row.getCell(cID + 1);
            if (cNext != null) {
                Cell cNew = row.createCell(cID, cNext.getCellTypeEnum());
                cloneCell(cNew, cNext);
                //Set the column width only on the first row.
                //Other wise the second row will overwrite the original column width set previously.
                if(rId == 0) {
                    sheet.setColumnWidth(cID, sheet.getColumnWidth(cID + 1));

                }
            }
        }
    }
}

private void cloneCell(Cell cNew, Cell cOld) {
    cNew.setCellComment(cOld.getCellComment());
    cNew.setCellStyle(cOld.getCellStyle());

    if (CellType.BOOLEAN == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getBooleanCellValue());
    } else if (CellType.NUMERIC == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getNumericCellValue());
    } else if (CellType.STRING == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getStringCellValue());
    } else if (CellType.ERROR == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getErrorCellValue());
    } else if (CellType.FORMULA == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getCellFormula());
    }
}
</code>```

OwnerDisplayName: VGS

Title: Apache POI xls column Remove

Body:
codewing's solution worked for me like a charm with the following minor changes:

When we clone the cell, the call should be cloneCell(cNew, cNext)

We should set the column width only for the first row.

I'm using version 3.17 of the api, so a few things changed (like CellType changed from int to an enum).

Full code is below (for clarity):

private void deleteColumn(Sheet sheet, int columnToDelete) {
    for (int rId = 0; rId < sheet.getLastRowNum(); rId++) {
        Row row = sheet.getRow(rId);
        for (int cID = columnToDelete; cID < row.getLastCellNum(); cID++) {
            Cell cOld = row.getCell(cID);
            if (cOld != null) {
                row.removeCell(cOld);
            }
            Cell cNext = row.getCell(cID + 1);
            if (cNext != null) {
                Cell cNew = row.createCell(cID, cNext.getCellTypeEnum());
                cloneCell(cNew, cNext);
                //Set the column width only on the first row.
                //Other wise the second row will overwrite the original column width set previously.
                if(rId == 0) {
                    sheet.setColumnWidth(cID, sheet.getColumnWidth(cID + 1));

                }
            }
        }
    }
}

private void cloneCell(Cell cNew, Cell cOld) {
    cNew.setCellComment(cOld.getCellComment());
    cNew.setCellStyle(cOld.getCellStyle());

    if (CellType.BOOLEAN == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getBooleanCellValue());
    } else if (CellType.NUMERIC == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getNumericCellValue());
    } else if (CellType.STRING == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getStringCellValue());
    } else if (CellType.ERROR == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getErrorCellValue());
    } else if (CellType.FORMULA == cNew.getCellTypeEnum()) {
        cNew.setCellValue(cOld.getCellFormula());
    }
}


问:
Reference outside the sealed class in Kotlin?
I'm trying to create a class that uses its own state to operate on the state of an external object that it holds a reference to.  The external object can be of class A or B, which are similar, but not controlled by the author.  So a sealed class is created to access their common attributes, per this earlier answer from @SimY4.

```<code>// *** DOES NOT COMPILE ***
class A {   // foreign class whose structure is not modifiable
  val prop get()= "some string made the Class-A way"
}
class B {   // foreign class whose structure is not modifiable
  val prop get()= "some string made the Class-B way"
}
data class ABTool (val obj:AB, val i:Int, val j:Int) {
  // class that manipulates i and j and uses them to do
  // things with AB's "common" attributes through the sealed class AB
  sealed class AB {   // substitute for a common interface
    abstract val prop: String
    abstract val addmagic: String
    data class BoxA(val o:A) : AB() {
      override val prop get()= o.prop
      override val addmagic get() = prop + this@???.magic  // HOW TO REFERENCE?
    }
    data class BoxB(val o:B) : AB() {
      override val prop get()= o.prop
      override val addmagic get() = this@???.magic + prop  // HOW TO REFERENCE?
    }
  }
  val magic get()= "magic: ${i*j}"
}
</code>```

The problem now is that I've figured out I can't operate on the external object in the way I want, because a sealed class can't refer to its outer class members.  Is there a better way to make this work, even if using a different approach (other than sealed class), while:

not changing foreign classes A or B;

respecting that A and B (and many others in the real case) are similar, so I'm trying to write one tool that calculates and adds magic to A and B with the same code base; and

noting that although the ABTool tools are the same, the way they are applied to add magic is slightly different in A vs. B, just as the to access the conceptually common elements of A and B may be different.

Any thoughts on this or a similar workaround?  Maybe a more functional approach that I haven't conceived yet?

答:
If ABTool being a sealed class is something you can give up, then here's a solution:

Replace sealed with inner abstract at the ABTool declaration;

Mark BoxA and BoxB as inner as well;

```<code>data class ABTool(val obj: AB, val i: Int, val j: Int) {
    inner abstract class AB {
        abstract val prop: String
        abstract val addmagic: String

        inner class BoxA(val o: A) : AB() {
            override val prop get() = o.prop
            override val addmagic get() = prop + magic
        }

        inner class BoxB(val o: B) : AB() {
            override val prop get() = o.prop
            override val addmagic get() = magic + prop
        }
    }

    val magic get() = "magic: ${i * j}"
}
</code>```

(alternatively, instead of marking AB as inner, move BoxA and BoxB out of it to the scope of ABTool)

Body:
I'm trying to create a class that uses its own state to operate on the state of an external object that it holds a reference to.  The external object can be of class A or B, which are similar, but not controlled by the author.  So a sealed class is created to access their common attributes, per this earlier answer from @SimY4.

// *** DOES NOT COMPILE ***
class A {   // foreign class whose structure is not modifiable
  val prop get()= "some string made the Class-A way"
}
class B {   // foreign class whose structure is not modifiable
  val prop get()= "some string made the Class-B way"
}
data class ABTool (val obj:AB, val i:Int, val j:Int) {
  // class that manipulates i and j and uses them to do
  // things with AB's "common" attributes through the sealed class AB
  sealed class AB {   // substitute for a common interface
    abstract val prop: String
    abstract val addmagic: String
    data class BoxA(val o:A) : AB() {
      override val prop get()= o.prop
      override val addmagic get() = prop + this@???.magic  // HOW TO REFERENCE?
    }
    data class BoxB(val o:B) : AB() {
      override val prop get()= o.prop
      override val addmagic get() = this@???.magic + prop  // HOW TO REFERENCE?
    }
  }
  val magic get()= "magic: ${i*j}"
}

The problem now is that I've figured out I can't operate on the external object in the way I want, because a sealed class can't refer to its outer class members.  Is there a better way to make this work, even if using a different approach (other than sealed class), while:

not changing foreign classes A or B;

respecting that A and B (and many others in the real case) are similar, so I'm trying to write one tool that calculates and adds magic to A and B with the same code base; and

noting that although the ABTool tools are the same, the way they are applied to add magic is slightly different in A vs. B, just as the to access the conceptually common elements of A and B may be different.

Any thoughts on this or a similar workaround?  Maybe a more functional approach that I haven't conceived yet?

Body:
If ABTool being a sealed class is something you can give up, then here's a solution:

Replace sealed with inner abstract at the ABTool declaration;

Mark BoxA and BoxB as inner as well;

data class ABTool(val obj: AB, val i: Int, val j: Int) {
    inner abstract class AB {
        abstract val prop: String
        abstract val addmagic: String

        inner class BoxA(val o: A) : AB() {
            override val prop get() = o.prop
            override val addmagic get() = prop + magic
        }

        inner class BoxB(val o: B) : AB() {
            override val prop get() = o.prop
            override val addmagic get() = magic + prop
        }
    }

    val magic get() = "magic: ${i * j}"
}

(alternatively, instead of marking AB as inner, move BoxA and BoxB out of it to the scope of ABTool)


问:
clingo apply a variable range
I do not know much about clingo, i hope i can share the problem clearly.

Currently i've

```<code>cellUseCount(X,Y,C) :- C = #count{cell(X,Y)}, target(X,Y,XX,YY).
</code>```

which returns results for each X,Y value. How can i get for X-XX and Y-YY range.

For instance: the rule generates cellUseCount(1,5,1) for target(1,5,1,1). However, i'd like to have cellUseCount(1,1,1), cellUseCount(1,2,1), cellUseCount(1,3,1), cellUseCount(1,4,1), cellUseCount(1,5,1) for target(1,5,1,1). How can i implement that?

Thanks in advance.

答:
Try:

```<code>% instance
target(1,5,1,1).

% encoding
cellUseCount(X,1..Y,C) :- C = #count{cell(X,Y)}, target(X,Y,XX,YY).
</code>```

Output:

Title: clingo apply a variable range

Body:
I do not know much about clingo, i hope i can share the problem clearly.

Currently i've

cellUseCount(X,Y,C) :- C = #count{cell(X,Y)}, target(X,Y,XX,YY).

which returns results for each X,Y value. How can i get for X-XX and Y-YY range.

For instance: the rule generates cellUseCount(1,5,1) for target(1,5,1,1). However, i'd like to have cellUseCount(1,1,1), cellUseCount(1,2,1), cellUseCount(1,3,1), cellUseCount(1,4,1), cellUseCount(1,5,1) for target(1,5,1,1). How can i implement that?

Thanks in advance.

Body:
Try:

% instance
target(1,5,1,1).

% encoding
cellUseCount(X,1..Y,C) :- C = #count{cell(X,Y)}, target(X,Y,XX,YY).

Output:


问:
Custom gradle task gets executed even during sync
So I am sure this is something very dumb mistake, but I need your help since I am not a gradle expert.

TASK:
read versionCode from file add +1 to it and save it back.

```<code>task executeOrderSixtySix {
def versionPropsFile = file('versionCodes.properties')
if (versionPropsFile.canRead()) {
    def Properties versionProps = new Properties()

    versionProps.load(new FileInputStream(versionPropsFile))

    def versionNumber = versionProps['DEV_VERSION'].toInteger() + 1
    versionProps['DEV_VERSION'] = versionNumber.toString()
    versionProps.store(versionPropsFile.newWriter(), null)

    //     'assembleDebug'

} else {
    throw new GradleException("Nyeeeh on versionCodes.properties!")
}}
</code>```

So when I have to do an internal drop I would like to run this task first, increase the devVersion number by 1 and then run the 'assemble' task to build all artifacts.

PROBLEM:
This task executes itself, even if I just sync the cradle file causing increased versionCode all the time.

I don't want to increase the versionCode during sync, development build only just for QAdrop, when I also have to assemble every APK.

Could you please help me out and tell me why is this task getting called/executed and who can I prevent it?

答:
You need a doLast block inside of your task block. build.gradle file is a configuration script so it reads as declare the task when on configuration and declare the action on the execution.

Anything done in the task either before or after the doLast block would be run during configuration time. The code in the doLast block itself runs at execution time.

```<code>task executeOrderSixtySix {
    doLast {
        def versionPropsFile = file('versionCodes.properties')
        if (versionPropsFile.canRead()) {
            def Properties versionProps = new Properties()

            versionProps.load(new FileInputStream(versionPropsFile))

            def versionNumber = versionProps['DEV_VERSION'].toInteger() + 1
            versionProps['DEV_VERSION'] = versionNumber.toString()
            versionProps.store(versionPropsFile.newWriter(), null)

            //     'assembleDebug'

        } else {
            throw new GradleException("Nyeeeh on versionCodes.properties!")
        }
    }
}
</code>```

Ref: https://www.oreilly.com/learning/write-your-own-custom-tasks-in-gradle

Body:
So I am sure this is something very dumb mistake, but I need your help since I am not a gradle expert.

TASK:
read versionCode from file add +1 to it and save it back.

task executeOrderSixtySix {
def versionPropsFile = file('versionCodes.properties')
if (versionPropsFile.canRead()) {
    def Properties versionProps = new Properties()

    versionProps.load(new FileInputStream(versionPropsFile))

    def versionNumber = versionProps['DEV_VERSION'].toInteger() + 1
    versionProps['DEV_VERSION'] = versionNumber.toString()
    versionProps.store(versionPropsFile.newWriter(), null)

    //     'assembleDebug'

} else {
    throw new GradleException("Nyeeeh on versionCodes.properties!")
}}

So when I have to do an internal drop I would like to run this task first, increase the devVersion number by 1 and then run the 'assemble' task to build all artifacts.

PROBLEM:
This task executes itself, even if I just sync the cradle file causing increased versionCode all the time.

I don't want to increase the versionCode during sync, development build only just for QAdrop, when I also have to assemble every APK.

Could you please help me out and tell me why is this task getting called/executed and who can I prevent it?

Body:
You need a doLast block inside of your task block. build.gradle file is a configuration script so it reads as declare the task when on configuration and declare the action on the execution.

Anything done in the task either before or after the doLast block would be run during configuration time. The code in the doLast block itself runs at execution time.

task executeOrderSixtySix {
    doLast {
        def versionPropsFile = file('versionCodes.properties')
        if (versionPropsFile.canRead()) {
            def Properties versionProps = new Properties()

            versionProps.load(new FileInputStream(versionPropsFile))

            def versionNumber = versionProps['DEV_VERSION'].toInteger() + 1
            versionProps['DEV_VERSION'] = versionNumber.toString()
            versionProps.store(versionPropsFile.newWriter(), null)

            //     'assembleDebug'

        } else {
            throw new GradleException("Nyeeeh on versionCodes.properties!")
        }
    }
}

Ref: https://www.oreilly.com/learning/write-your-own-custom-tasks-in-gradle