web-dev-qa-db-ja.com

クラスのコンストラクター関数で値を返す

これまでのところ、コンストラクタを持つPHPクラスがあります

public function __construct ($identifier = NULL)
{
 // Return me.
if ( $identifier != NULL )
{
  $this->emailAddress = $identifier;
  if ($this->loadUser() )
    return $this;      
  else
  {
// registered user requested , but not found ! 
return false;
  }
}

loadUserの機能は、特定のメールアドレスのデータベースを検索することです。データベースにないことが確実なメールに識別子を設定すると、最初のIFが渡され、最初のELSEに進みます。ここで、コンストラクタはFALSEを返す必要があります。しかし、代わりに、すべてのNULL値を持つクラスのオブジェクトを返します!

どうすればこれを防ぐことができますか?ありがとう

編集:

答えてくれてありがとう。それは非常に速かったです! OOPの方法は例外をスローすることです。だから、スローの場合、私の質問は例外で何をすべきかを変更しますか?? php.netのマニュアルはかなり紛らわしいです!

    // Setup the user ( we assume he is a user first. referees, admins are   considered users too )
    try { $him = new user ($_emailAddress);
    } catch (Exception $e_u) { 
      // try the groups database
      try { $him = new group ($_emailAddress); 
      } catch (Exception $e_g) {
          // email address was not in any of them !!  
        }
    }
46
Anoosh Ravan

コンストラクターは戻り値を取得しません。それらは完全にクラスをインスタンス化するのに役立ちます。

既に実行していることを再構築せずに、ここで例外を使用することを検討できます。

public function __construct ($identifier = NULL)
{
  $this->emailAddress = $identifier;
  $this->loadUser();
}

private function loadUser ()
{
    // try to load the user
    if (/* not able to load user */) {
        throw new Exception('Unable to load user using identifier: ' . $this->identifier);
    }
}

これで、この方法で新しいユーザーを作成できます。

try {
    $user = new User('[email protected]');
} catch (Exception $e) {
    // unable to create the user using that id, handle the exception
}
71
erisco

コンストラクタは、オブジェクトを作成することを想定しています。 PHPではブール値はオブジェクトと見なされないため、唯一のオプションはnullです。それ以外の場合、回避策を使用します。つまり、実際のオブジェクトを作成する静的メソッドを記述します。

public static function CheckAndCreate($identifier){
  $result = self::loadUser();
  if($result === true){
    return new EmailClassNameHere();
  }else{
    return false;
  }
}
8
worenga

あなたができる最善のことは、スティーブが提案したことです。オブジェクトプロパティにコンストラクタパラメータを割り当てる以外の作業を行うコンストラクタを作成しないでください。デフォルトのパラメータを作成することはできますが、それ以外は作成しないでください。コンストラクタは、完全に機能するオブジェクトを作成するためのものです。このようなオブジェクトは、インスタンス化後は常に期待どおりに機能する必要があります。ユーザーには、電子メール、名前、およびおそらく他のプロパティがあります。ユーザーオブジェクトをインスタンス化する場合は、それらのすべてのプロパティをコンストラクターに渡します。例外を投げることも良い方法ではありません。例外は、例外的な条件下でスローされることを意図しています。最終的にそのようなユーザーが存在しないことがわかったとしても、電子メールでユーザーを要求することは例外ではありません。たとえば、email = ''でユーザーを要求した場合は例外になります(システムで通常の状態である場合を除き、idはむしろそのような場合にnullのメールを提案します)。ユーザーオブジェクトのこれらのプロパティをすべて取得するには、ファクトリ(または必要に応じてリポジトリ)オブジェクト(はい、オブジェクト-静的なものを使用するのは悪い習慣です)プライベートコンストラクターは悪い習慣です(あなたはとにかく静的メソッドが必要で、すでに述べたように、静的は非常に悪いです)

結果は次のようになります。

class User {
  private $name;
  private $email;
  private $otherprop;

  public function __construct($name, $email, $otherprop = null) {
    $this->name = $name;
    $this->email = $email;
    $this->otherprop = $otherprop;
  }
}

class UserRepository {
  private $db;

  public function __construct($db) {
    $this->db = $db; //this is what constructors should only do
  }

  public function getUserByEmail($email) {
    $sql = "SELECT * FROM users WHERE email = $email"; //do some quoting here
    $data = $this->db->fetchOneRow($sql); //supose email is unique in the db
    if($data) {
      return new User($data['name'], $data['email'], $data['otherprop']);
    } else {
      return null;
    }
  }
}

$repository = new UserRepository($database); //suppose we have users stored in db
$user = $repository->getUserByEmail('[email protected]');
if($user === null) {
  //show error or whatever you want to do in that case
} else {
  //do the job with user object
}

見る?静的なし、例外なし、シンプルなコンストラクター、非常に読みやすく、テスト可能かつ変更可能

7
slepic

コンストラクターは、作成しようとしているオブジェクト以外は何も返すことができません。インスタンス化が適切に完了しない場合、発見したNULLプロパティでいっぱいのクラスインスタンスが残ります。

オブジェクトが不完全またはエラー状態でロードされる場合、それを示すためにプロパティを設定することをお勧めします。

// error status property
public $error = NULL;

public function __construct ($identifier = NULL)
{
 // Return me.
if ( $identifier != NULL )
{
  $this->emailAddress = $identifier;
  if (!$this->loadUser() )
  {
   // registered user requested , but not found ! 
   $this->error = "user not found";
  }
}

オブジェクトをインスタンス化するときに、エラーステータスがあるかどうかを確認できます。

$obj = new MyObject($identifier);
if (!empty($obj->error)) {
   // something failed.
}

別の(おそらくより良い)代替手段は、コンストラクターで例外をスローし、try/catchでインスタンス化をラップすることです。

3

なぜコンストラクタを時々失敗させるのではなく、オブジェクトを構築するために必要なコンストラクタに結果を単純に渡さないのですか?

時々失敗させることができたとしても、実際にdidコンストラクトを確認するためにコンストラクターを呼び出した後にチェックする必要があり、それらの行では-> loadUser()を呼び出して渡すことができます結果をコンストラクターに入れます。

誰かが私に言った良いヒントは、「コンストラクターは常にオブジェクトを構築するために必要なものを提供します。オブジェクトを探しに行かせないでください。」

public function __construct ($emailInTheDatabase, $otherFieldNeeded)
{
    $this->emailAddress = $emailInTheDatabase;
    $this->otherField = $otherFieldNeeded;
}
2
Steve

すべてのコメントと解決策に感謝します。ここに私が問題を解決するためにしたことです:(他の人を助けることを願っています)

// Setup the user ( we assume he is a user first. referees, admins are considered users too )
    try {
      $him = new user ($_emailAddress); 
      // check the supplied password 
      $pass_ok = $him->auth($_Password);

      // check the activation status 
      $active_ok = $him->makeActive();

    } catch (Exception $e_u) { 
      // try the groups database
      try { 
      $him = new group ($_emailAddress);
      // check the supplied password 
      $pass_ok = $him->auth($_Password);
              //var_dump ($pass_ok);

      // check the activation status 
      $active_ok = $him->makeActive();
      } catch (Exception $e_g) {
          // email address was not in any of them !!
          $pass_ok = false; $active_ok = false;
        }
    }
0
Anoosh Ravan

コンストラクトにはあまり入れません。すべてをコンストラクターに入れるのではなく、ユーザー(ファクトリー)を作成する静的関数を考慮する必要があります。したがって、ロード関数を暗黙的に呼び出す必要なく、ユーザーオブジェクトを使用できます。これにより、痛みが軽減されます。

public function __construct(){}

public function setIdentifier($value){
    $this->identifier = $value;
}

public function load(){
    // whatever you need to load here
    //...
    throw new UserParameterNotSetException('identifier not set');
    // ...
    // if user cannot be loaded properly
    throw new UserNotFoundException('could not found user');
}

public static function loadUser($identifier){
    $user = new User();
    $user->setIdentifier($identifier);
    $user->load();
    return $user;
}

サンプル使用法:

$user = new User(); 
try{
    $user->setIdentifier('identifier');
    $user->load();
}
catch(UserParameterNotSetException $e){
    //...
}
catch(UserNotFoundException $e){
    // do whatever you need to do when user is not found
}

// With the factory static function:
try{
    $user2 = User::loadUser('identifier');
}
catch(UserParameterNotSetException $e){
    //...
}
catch(UserNotFoundException $e){
    // do whatever you need to do when user is not found
}
0
Nico

4年間、2万2,000人の視聴者がプライベートコンストラクターと、次のようなオブジェクトを作成しようとするメソッドを作成することを提案していないことに本当に驚いています。

class A {
    private function __construct () {
        echo "Created!\n";
    }
    public static function attemptToCreate ($should_it_succeed) {
        if ($should_it_succeed) {
            return new A();
        }
        return false;
    }
}

var_dump(A::attemptToCreate(0)); // bool(false)
var_dump(A::attemptToCreate(1)); // object(A)#1 (0) {}
//! new A(); - gives error

これにより、オブジェクトまたはfalseを取得できます(nullを返すようにすることもできます)。両方の状況をキャッチするのが非常に簡単になりました。

$user = User::attemptToCreate('[email protected]');
if(!$user) { // or if(is_null($user)) in case you return null instead of false
    echo "Not logged.";
} else {
    echo $user->name; // e.g.
}

ここでテストできます: http://ideone.com/TDqSyi

私のソリューションは、例外をスローしてキャッチするよりも使いやすいと思います。

0
Al.G.