質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

Fatal error: Call to a member function prepare() on stringを解決したい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 4,712

score 12

現在タイムライン掲示板を作成しており、実装が全て終えたのでリファクタリングをしています。
リファクタリングをする項目は以下です。

1.サインインユーザー情報取得
2.投稿情報登録処理
3.いいね件数
3.いいね済みかどうか

2までは無事終えたのですが、3.いいね件数をリファクタリングするとFatal error: Call to a member function prepare() on string in /Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/function.php
のエラーがfunction.phpの1サインインユーザー情報取得のリファクタリングに出ます。

他の類似エラーを参考に、文頭に
error_reporting(E_ALL);
ini_set('display_errors', '1');
を追記しましたがエラー表示は変わらぬままでした。

リファクタリングをしない場合だと、同じ場所にエラーが出ないのになぜでしょうか…
ご教示お願い致します。

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

session_start();
require('dbconnect2.php');
require('function.php');

// ユーザー登録(リファクタリングする前)
// $sql = 'SELECT * FROM `users` WHERE `id`=?';
// $data = [$_SESSION['LearnSNS_self']['id']];
// $stmt = $dbh->prepare($sql);
// $stmt->execute($data);
// $signin_user = $stmt->fetch(PDO::FETCH_ASSOC);

// ユーザー登録(リファクタリングした後)
$signin_user = getUser($dbh,$_SESSION['LearnSNS_self']['id']);

------

// ユーザーが投稿ボタンを押したとき
if(!empty($_POST)){
    $feed = $_POST['feed'];

    if($feed != ''){
     (リファクタリングする前)
        // $sql = 'INSERT INTO `feeds` (`feed`, `user_id`, `created`) VALUES(?,?,NOW())';
        // $data = [$feed, $signin_user['id']];
        // $stmt = $dbh->prepare($sql);
        // $stmt->execute($data);
     (リファクタリングした後)
        createFeed($dbh,$feed,$signin_user['id']);

        header('location: timeline.php');
        exit();
    }else{
        $errors['feed'] = 'blank';
    }
}
------
// 何件いいねされているか確認(リファクタリングする前)
        // $like_sql = 'SELECT COUNT(*)AS `like_cnt` FROM `likes` WHERE `feed_id`=?';
        // $like_data = [$record['id']];
        // $like_stmt = $dbh->prepare($like_sql);
        // $like_stmt->execute($like_data);
        // $like = $like_stmt->fetch(PDO::FETCH_ASSOC);
        // $record['like_cnt'] = $like['like_cnt'];
     (リファクタリングした後)
        $record['like_cnt'] = countLike($dbh,$feed_id);
<?php



// サインユーザー情報取得
function getUser($dbh, $id) {
    $sql = 'SELECT * FROM `users` WHERE `id`=?';
    $data = [$id];
    $stmt = $dbh->prepare($sql);###←★エラーが出ていると言われる場所
    $stmt->execute($data);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}


// // 投稿情報登録処理
function createFeed($dbh,$feed,$user_id){
    $sql = 'INSERT INTO `feeds`(`feed`,`user_id`,`created`) VALUES(?,?,NOW())';
    $data = [$feed,$signin_user['id']];
    $stmt = $dbh->prepare($sql);
    $stmt->execute($data);
}

// いいね件数
$dbh = "";
$feed_id = "";
function countLike(){
    $sql = "SELECT COUNT(*)AS `like_cnt` FROM `likes` WHERE `feed_id`=?";
    $data = [$feed_id];
    $stmt = $dbh->prepare($sql);
    $stmt->execute($data);
    $result = $like_stmt->fetch(PDO::FETCH_ASSOC);
    return $result['like_cnt'];
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2019/02/12 14:18

    [ ]### ←★エラーが出ていると言われる場所
    ↑全角空白が入っています。再現確認する上での弊害となるので調整いただけたらと。

    キャンセル

回答 3

+5

$dbh = "";

ここで文字列を代入した$dbhに対して、メソッドが呼ばれてしまっているようです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/13 11:21

    ご回答ありがとうございます!

    キャンセル

+2

ちゃんとやるならclassで機能をわけた方がよいでしょう
$dbhがきちんと設定されていなければそのメソッドである
prepareは実行できません

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/13 11:23

    すみません、まだ初級者なので$dbh設定をわかっていませんでした。
    ありがとうございます!

    キャンセル

checkベストアンサー

+1

別観点から(重複するところもあるけど)
countLike()は$dbhも$feed_idも渡してないので動きませんが大丈夫ですか?

yambejpさんの回答も含めて共通化はある程度きちんと考えた方が良いです。

$dbhはおそらくdbconnect2.phpに書かれているものだと思いますが、おおおよそnew PDO()の結果ですよね。
引数は基本的に「処理によって変わるもの」を渡すべきと考えます。「変数」ですし。
接続情報自体はそもそも場合によって変わるわけではありませんので、毎回引数で渡すくらいならglobalキーワードで持っていったほうがまだ自然にも思います。

本来やるべき姿としてはnew PDO()と同じような構成ではないかなと。
newするときの引数でコンストラクタによって接続を確立して、その後、prepare()なりなんなり機能を利用していますよね?
それと同じく、「DB関係の処理をまとめたクラス」を作って、それぞれ役割を渡すべきではないかなと。
PDOと同じくコンストラクタで接続を確立しておいてプロパティに接続情報を持っておけばそれを参照するように各function冒頭で書いておけば毎回引数で渡すようなことはしなくて済みますし。

※ざっくり作ったので動作未検証。本来は各functionのアクセス権とか引数と返り値の型宣言まできちんと入れるべき

class db{
    protected $dbh = null;

    function __construct(){
        try{
            $this->dbh= new PDO("XXX");//ここは元の接続処理をそのまま移行していいかなと
        }catch(PDOException $e){
            var_dump($e);
        }

    }

    // サインユーザー情報取得
    function getUser($id) {
        $sql = 'SELECT * FROM `users` WHERE `id`=?';
        $data = [$id];
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute($data);
        return $stmt->fetch(PDO::FETCH_ASSOC);//PDO::FETCH_ASSOCしかしないのであれば毎回書くのではなくそれだけで1つ関数作ってreturn $stmt->fetch(PDO::FETCH_ASSOC);するだけの関数作るのもあり。
    }


    // 投稿情報登録処理
    function createFeed($feed,$user_id){
        $sql = 'INSERT INTO `feeds`(`feed`,`user_id`,`created`) VALUES(?,?,NOW())';
        $data = [$feed,$signin_user['id']];
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute($data); //何も返さないのはNG。insertは確か成功したかどうかが返るはずなので何かしら返したほうがいいし「確実に成功する」前提での作りは良くないのでこちらもtry-catchで囲ってしっかりExceptionを捕捉すべき
    }

    // いいね件数
    function countLike($feed_id){
        $sql = "SELECT COUNT(*)AS `like_cnt` FROM `likes` WHERE `feed_id`=?";
        $data = [$feed_id];
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute($data);
        $result = $stmt->fetch(PDO::FETCH_ASSOC); //元のコード、変数名間違えてますよ。修正しました。
        return $result['like_cnt'];
    }
    /*
SQL作る→$dataに?の分の変数作る→preareする→executeに$data渡して実行
まで同じならここも「executeするまでの処理」を共通化したものを作ったほうがスッキリすると思う。
要は$sql作って、$dataを配列で渡せば良いので。
同じ処理が2回3回以上でてくるようなら共通化を検討しましょう。
*/
}


使うとき

$db = new db();


$signin_user = $db->getUser($_SESSION['LearnSNS_self']['id']);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/13 12:06

    ご回答、丁寧に解説していただきありがとうございます!
    すみません、まだ初心者ですので追加で質問させてください。


    mts10806さんが示してくださったclass dbの$this->dbh= new PDO("XXX");には何を入れるのが正解なのでしょうか?
    mts10806さんのおっしゃるとおり、dbconnect2.phpには
    $dsn = 'mysql:dbname=LearnSNS_self;host=localhost';
    $user = 'root';
    $password='';
    $dbh = new PDO($dsn, $user, $password);
    // SQL文にエラーがあった際、画面にエラーを出力する設定
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->query('SET NAMES utf8');
    で接続情報を書いています。

    $this->dbh= new PDO("dbconnect2.php");にすると
    エラー:object(PDOException)#3 (8) { ["message":protected]=> string(24) "invalid data source name" ["string":"Exception":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(72) "/Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/function.php" ["line":protected]=> int(8) ["trace":"Exception":private]=> array(2) { [0]=> array(6) { ["file"]=> string(72) "/Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/function.php" ["line"]=> int(8) ["function"]=> string(11) "__construct" ["class"]=> string(3) "PDO" ["type"]=> string(2) "->" ["args"]=> array(1) { [0]=> string(14) "dbconnect2.php" } } [1]=> array(6) { ["file"]=> string(72) "/Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/timeline.php" ["line"]=> int(16) ["function"]=> string(11) "__construct" ["class"]=> string(2) "db" ["type"]=> string(2) "->" ["args"]=> array(0) { } } } ["previous":"Exception":private]=> NULL ["errorInfo"]=> NULL }
    Fatal error: Call to undefined method db::getUser() in /Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/timeline.php on line 18
    がでます。
    $this->dbh= new PDO($dsn, $user, $password);にすると上記のエラーに加え、
    Notice: Undefined variable: dsn in /Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/function.php on line 8

    Notice: Undefined variable: user in /Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/function.php on line 8
    と、また定義されていないと言われます。

    ②class dbはfunction.phpの中で間違ってないでしょうか。

    ③いいね件数取得の
    feed_id = "";
    dbh = "";
    は残したままでよいでしょうか?


    かなり質問が多くなってしまい申し訳ないです。
    エラーを読んだのですが難しくて…。
    よろしくお願い致します。

    キャンセル

  • 2019/02/13 12:53


    省略していますので自身の接続情報を入れてください。
    PDO("dbconnect2.php")ではないです。dbconnect2.phpという文字列を入れても何も起きません。
    dbconnect2.phpの中に書かれている下記をそのまま置き換えればよいでしょう。
    $this->dbh= new PDO("XXX");

    $dsn = 'mysql:dbname=LearnSNS_self;host=localhost';
    $user = 'root';
    $password='';
    $this->dbh = new PDO($dsn, $user, $password);

    できればclass名とファイル名はあわせてください。
    というか役割によってファイルをわけるようにしてください。
    functions.phpだと全部盛りになってしまってなんでもかんでも
    そこに放り込んでいくとファットコード(何かよくわからないけど巨大なすんごい行数のコード)になります。

    不要です。使ってませんよね。残しておく意味はないです。

    キャンセル

  • 2019/02/13 14:12

    ありがとうございます!
    先ほどのエラーたちはなくなりました。
    最初に示してくださった
    ---
    $db = new db();
    $signin_user = $db->getUser($_SESSION['LearnSNS_self']['id']);
    ---
    は、timeline.phpのサインインユーザーであっていますか?
    エラー:
    Fatal error: Call to undefined method db::getUser() in /Applications/XAMPP/xamppfiles/htdocs/batch48/LearnSNS_self/timeline.php on line 18
    が出てしまいました。
    こちら↓を参考にしたのですが、$dbはちゃんと定義はしてあると思うのですが…
    https://marycore.jp/prog/php/call-to-undefined-method/

    キャンセル

  • 2019/02/13 14:38

    >timeline.phpのサインインユーザーであっていますか?
    知りません。timeline.phpが提示されていませんし。

    現在の質問者さんのコードが分かりませんのでこれだけでは何とも言えません。
    もし本件が解決したのでしたら、一度この質問を閉じられて、新しい質問を立ててこちらの質問を関連質問としてリンクを置かれてはどうでしょうか。

    コメントを見る限りだと「完全に作り直しである」という認識がないまま、適当に自分のコードを残してしまっているような気がします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る