2012-06-26 43 views
5

Tôi đang làm việc trên một giải pháp ORM đơn giản và đã gặp phải một tình huống khó khăn. Lý tưởng nhất, tôi muốn có thể sử dụng các phương thức trong cả ngữ cảnh tĩnh và bối cảnh đối tượng tùy thuộc vào cách nó được gọi. Tôi không chắc chắn nếu điều này là có thể, nhưng đây là những gì tôi muốn nói:Phương pháp tĩnh/không tĩnh Vấn đề

Nói một mô hình Người dùng muốn gọi nơi() tĩnh, điều này hiện đang hoạt động tốt, ví dụ:

$user = User::where('id = ?', 3); 

Bây giờ, tôi cũng hỗ trợ các mối quan hệ, ví dụ như một người dùng có thể có tin nhắn. Khi mối quan hệ này được thiết lập, tôi chỉ cần lưu trữ một bản sao trống của một mô hình tin nhắn trong mô hình người dùng và thiết lập một khóa ngoại. Ví dụ:

$user -> messages = new Message(); 
$user -> messages -> foreign_key = 'user_id'; 

Bây giờ, lý tưởng, tôi muốn để có thể gọi:

$user -> messages -> where('unread = ?', 1); 

Trong một bối cảnh không tĩnh và tận dụng $ this -> foreign_key khi trong bối cảnh này nên chỉ để kéo tin nhắn trong đó khóa ngoại khớp với id của người dùng. Đây có phải là loại chuyển đổi ngữ cảnh có thể có trong PHP không? Bất kỳ tham chiếu đến $ này từ ngữ cảnh tĩnh ném lỗi như một phương thức tĩnh và không nên dựa vào $ this (vì lý do hiển nhiên, khi được gọi từ ngữ cảnh tĩnh, $ this sẽ không tồn tại)

Có bất kỳ cách thông minh xung quanh điều này? Tôi đã cố gắng quá tải phương pháp để có hai nguyên mẫu khác nhau, cả hai có và không có từ khóa tĩnh nhưng điều này đã ném một lỗi khai báo lại.

+0

$ user = Người dùng :: find(); bạn muốn điều này trả lại nếu nó không chỉ định khóa ngoại? – craig1231

+0

Nó có vẻ như trong sử dụng hàng đầu, 'find()' là một hàm (tĩnh) trong lớp 'User', nhưng ở phía dưới,' find() 'là một phương thức trong lớp' Message'. Đó là cái nào? Cả hai? – jedwards

+0

Có lẽ đó là một ví dụ không rõ ràng, tôi đã bỏ qua các tham số vì chúng dường như không liên quan đến câu hỏi nhưng tôi sẽ làm rõ nó bằng cách trình bày chính xác hơn về cách nó thực sự hoạt động. –

Trả lời

4

Sau một chút chơi xung quanh, tôi đã tìm thấy cách để làm cho điều này có thể thực hiện được mà không có lỗi Strict Standards được đề cập bởi @ drew010. Tôi không thích nó, nó cảm thấy khủng khiếp, nhưng nó làm việc vì vậy tôi sẽ đăng bài này anyway.

Về cơ bản, ý tưởng là đặt phương thức bạn muốn truy cập privatestatic. Sau đó, bạn xác định các phương thức ma thuật __call()__callStatic() để chúng sẽ gọi phương thức tĩnh riêng tư. Bây giờ bạn có thể nghĩ rằng "điều này không giải quyết được vấn đề, tôi vẫn bị mắc kẹt trong một ngữ cảnh tĩnh" - mà bạn chỉ là một bổ sung nhỏ, bạn có thể thêm $this vào các đối số được chuyển đến phương thức thực tế trong __call() và tìm nạp làm đối số cuối cùng cho phương thức. Vì vậy, thay vì tham chiếu $this trong ngữ cảnh đối tượng, bạn tham chiếu đối số thứ ba để có được tham chiếu đến cá thể của riêng bạn.

Tôi có lẽ không giải thích điều này rất tốt, chỉ cần có một cái nhìn tại this code:

<?php 

class test_class { 

    private $instanceProperty = 'value'; 

    private static function where ($arg1, $arg2, $obj = NULL) { 
     if (isset($obj)) { 
      echo "I'm in an object context ($arg1, $arg2): I can access the instance variable: $obj->instanceProperty<br>\n"; 
     } else { 
      echo "I'm in a static context ($arg1, $arg2)<br>\n"; 
     } 
    } 

    public function __call ($method, $args) { 
     $method = "self::$method"; 
     if (is_callable($method)) { 
      $args[] = $this; 
      return call_user_func_array($method, $args); 
     } 
    } 

    public static function __callStatic ($method, $args) { 
     $method = "self::$method"; 
     if (is_callable($method)) { 
      return call_user_func_array($method, $args); 
     } 
    } 

} 

test_class::where('unread = ?', 1); 

$obj = new test_class(); 
$obj->where('unread = ?', 2); 
+0

Tôi thực sự khá thích giải pháp này, đây là cách thông minh xung quanh những hạn chế. Tôi không cần thiết như phá vỡ cách ngôn ngữ có nghĩa là để được sử dụng, nhưng điều này sẽ thêm một số đơn giản để các chương trình tổng thể. Khác với việc phá vỡ ý định ngôn ngữ tự nhiên, tôi cho rằng sẽ có một hit hiệu suất từ ​​việc sử dụng nó theo cách này, nhưng có thể đáng giá vì thực tế là chúng giống nhau trong mui xe là vô hình đối với người dùng cuối. Có bất kỳ lý do nào khác mà bạn đề xuất không sử dụng loại quá tải này không? –

+1

Lý do quan trọng nhất tôi sẽ không làm điều này là bởi vì khi tôi trở lại với nó trong hai năm tôi sẽ không biết mã này làm gì và tôi sẽ phải dành 10 phút để làm việc đó. Nếu bạn làm điều này, hãy bình luận tốt! Xét về hiệu năng, bạn đang nói về micro giây, nhưng bạn phải chú ý đến thực tế là mã ở trên cho phép truy cập vào mọi phương thức của đối tượng/lớp, công khai hoặc theo cách khác - vì vậy bạn có thể muốn kiểm tra '$ method' dựa vào danh sách trắng các phương thức được phép truy cập như thế này. – DaveRandom

+0

Bạn cũng có thể cần thực hiện một số xác thực bổ sung đối với số đối số được cung cấp, đặc biệt là trong ngữ cảnh đối tượng.Điều này là do PHP sẽ không phàn nàn về số lượng đối số không chính xác được truyền bởi vì nó không có định nghĩa cố định để kiểm tra nó, và vì bản chất của cách nó hoạt động bằng cách chắp thêm '$ this' vào các đối số, nếu bạn cung cấp sai số đối số đối tượng sẽ được chuyển đến tham số sai. – DaveRandom

4

Tôi không thể nghĩ ra bất kỳ cách nào để làm điều này mà không đi ngược lại các tiêu chuẩn PHP và sử dụng ngôn ngữ theo cách không được sử dụng.

Chức năng là tĩnh hoặc không. Có PHP cho phép bạn gọi nó theo một trong hai cách nhưng điều này vi phạm các tiêu chuẩn nghiêm ngặt và lý do duy nhất bạn có thể lấy đi khi thực hiện điều này là để tương thích ngược với mã PHP 4 cũ hơn mà tĩnh không tồn tại như một từ khóa.

xem xét mã này:

<?php 

class Test { 
    protected $_userId; 

    public function find() 
    { 
     if (isset($this)) { 
      echo "Not static.<br />\n"; 
     } else { 
      echo "Static.<br />\n"; 
     } 
    } 
} 

$t = new Test(); 
$t->find(); 

Test::find(); 

Đầu ra là:

Không tĩnh.
Tĩnh.

Nhưng với báo cáo lỗi bật, đây là sản phẩm thực tế:

Không tĩnh.

Tiêu chuẩn nghiêm ngặt: Phương pháp không tĩnh Kiểm tra :: tìm() không nên được gọi là tĩnh trong test.php trên dòng 19
Tĩnh.

Nếu bạn khai báo phương thức tĩnh, thì nó sẽ tĩnh bất kể nó được gọi như thế nào.

Vì vậy, tôi cho rằng câu trả lời là Có, bạn có thể thực hiện bằng cách sử dụng giải pháp này, nhưng tôi không khuyến nghị sử dụng. Nếu bạn muốn có cả hai cách, tôi sẽ đề xuất thêm hai phương pháp, public function find()public static function findStatic().

Vì mã của bạn sẽ được viết là $obj->find() hoặc Class::find(), bạn có thể dễ dàng sử dụng các phương pháp tĩnh và không tĩnh theo ý kiến ​​của tôi thay vì có một phương thức hoạt động tĩnh. Để tuân theo DRY, tôi giả sử một phương pháp sẽ tận dụng một phương pháp khác để thực hiện tìm kiếm thực tế.

+0

Cảm ơn câu trả lời của bạn, tôi đã đi đến kết luận này cũng như tôi có cùng các lỗi tiêu chuẩn nghiêm ngặt trong bài kiểm tra của mình. Đã hy vọng có thể sử dụng cùng một tên như tôi có 6-7 phương pháp khác nhau có thể được gọi là tất cả sẽ cần một tên truy cập tĩnh có tên. Nhưng điều này thực sự làm cho ý nghĩa hợp lý nhất, chỉ là hy vọng tôi có thể hack theo cách của tôi xung quanh nó;) –

4

Xin lỗi vì đã không trả lời câu hỏi của bạn, nhưng tôi có một số nhận xét không phù hợp với nhận xét .... Những gì bạn đang làm là một chút phi lý.

$user->messages = new Message(); 

Bạn đang tạo một đơn nhắn bên trong một biến gọi là điệp.
Bạn có nghĩa là $user->messages[] = new Message();?
Ngoài ra, hãy bảo vệ các biến lớp học của bạn.

$user->messages->where('unread = ?', 1); 

Tại đây bạn đang cố gắng chọn từ tin nhắn của người dùng, điều đó là vô nghĩa.
Bạn nên làm gì chỉ đơn giản là giống như bạn đã làm cho lớp User: có được những thông điệp tĩnh và sau đó giao cho người dùng của bạn:

$user->messages = Message::where('unread = ?', 1); 

Nếu bạn cần phải tìm kiếm cho thông điệp rằng có một chính cụ thể trọng điểm, vượt qua nó như một tham số cho phương thức where, có thể được tăng cường để mất nhiều khoản:

$messages = Message::where(array(
    array('unread = ?', 1), 
    array('id = ?',  $message->getID()), 
)); 

tôi cũng muốn thêm ghi chú cá nhân: tạo ra một ORM là một cách tuyệt vời để tìm hiểu, nhưng nếu bạn đang tìm kiếm một cái gì đó hơn nghiêm trọng, tôi khuyên bạn nên cung cấp cho Doctrine hoặc Propel một cái nhìn.

+0

Thật vậy, tôi đang làm điều này chủ yếu là để tìm hiểu. Để giải quyết sự kỳ quặc của việc lưu trữ Message() trong một $ user -> messages ... Tôi đã làm điều này khi tôi về cơ bản đang tìm cách bắt chước hành vi của User :: where() từ bên trong ngữ cảnh đối tượng. Làm như '$ user -> messages = Message; $ user -> messages :: find() 'không hoạt động, lưu trữ một bản sao trống của đối tượng và sử dụng các phương thức không tĩnh là gần nhất mà tôi có thể nghĩ đến. Nhưng những gì bạn đang nói có ý nghĩa, tôi chỉ muốn có một cách thông minh hơn để tận dụng mối quan hệ giữa họ mà không cần phải thông qua một cách rõ ràng khóa ngoại. –

+0

Bạn có thể chuyển đối tượng Message tới 'User :: addMessage()', và trong đó, chỉ lấy id của thông báo ('$ message-> getId()') và lưu nó khi bạn cần. - Khi bạn tìm nạp người dùng từ DB, sau đó bạn sẽ tự động nhận được tin nhắn của mình (* háo hức tải *) hoặc nhận tin nhắn khi người dùng yêu cầu họ (* tải chậm *). Hãy chắc chắn rằng bạn không yêu cầu họ lần thứ hai họ được gọi. –

+0

Ah vâng, điều này có vẻ như một giải pháp khá sạch sẽ là tốt. Tôi chắc chắn sẽ được chơi đùa với những khái niệm này tối nay để thử và tìm ra giải pháp tốt nhất, cảm ơn phản ứng của bạn! –

0

Từ quan điểm lý thuyết và thực tế, nó không phải là một ý tưởng tốt để có một phương pháp học mà có thể được gọi là cả hai từ bối cảnh tĩnh và không tĩnh.

Nếu bạn muốn đạt được khả năng truy cập của lớp/phương pháp của bạn trong suốt ứng dụng của bạn, có thể đó là một khởi đầu tốt để đọc về Dependency Injection, Service Container và chương trình định hướng phun phụ thuộc.

Bằng cách triển khai DI trong ứng dụng của bạn, bạn có thể sẽ mất bất kỳ nhu cầu nào về những gì bạn đã đề cập.

Tôi sẽ đề nghị điều tra trang web máng và bạn sẽ thấy rằng các cuộc gọi tĩnh trong ngữ cảnh mà bạn đang làm việc với tránh được và bị gắn cờ là thực hành không tốt. Trạng thái tĩnh/chia sẻ trong lập trình hướng đối tượng là một thứ cần tránh (cũng như mẫu đơn). Đừng làm cho tôi sai -> phương pháp tĩnh có mục đích và lợi ích của họ, tuy nhiên, cách bạn đang sử dụng nó là lỗi thời (mặc dù một số khung, như Laravel, thúc đẩy thực hành xấu này - ví dụ "Facades" và Eloquent) .

Các vấn đề liên quan