I am developing an app that has two name spaced differentiated folders.
Lets say
App/Http/Users/ and
App/Http/Drivers/
I have two api routes setup api.php and dapi.php.
The routes are also prefixed by localhost/api/foo and localhost/dapi/bar respectively.
Everything works ok but the issue is that there are some methods that I need to call for both. Such as save address info or call. Right now I have to make same controllers for both and duplicate a lot of code. What would be the best approach for this kind of project?
you should use traits
Traits are a mechanism for code reuse in single inheritance languages
such as PHP. A Trait is intended to reduce some limitations of single
inheritance by enabling a developer to reuse sets of methods freely in
several independent classes living in different class hierarchies. The
semantics of the combination of Traits and classes is defined in a way
which reduces complexity, and avoids the typical problems associated
with multiple inheritance and Mixins.
for example:
in your traite:
trait SameMethods {
function call() { /*1*/ }
function saveAddress() { /*2*/ }
}
.
namespace App\Http\Drivers;
class Foo extends Controller{
use SameMethods ;
/* ... */
}
.
namespace App\Http\Users;
class Bar extends Controller{
use SameMethods ;
/* ... */
}
Now you have these methods on your controllers.
another way is you have an another class for example ParentController extended from Controller that it contains same methods and foo and bar extends from this class
ParentController extends Controller {
function call() { /*1*/ }
function saveAddress() { /*2*/ }
}
.
namespace App\Http\Drivers;
class Foo extends ParentController {
/* ... */
}
.
namespace App\Http\Users;
class Bar extends ParentController {
/* ... */
}
Related
Normally I have a question about something not working, now I have a question about something that IS working, I am just confused as to why. This is the structure that I have in Laravel:
ExampleController
use App\Http\Traits\Trait1;
use App\Http\Traits\Trait2;
ExampleController extends Controller {
use Trait1, Trait2;
public function index()
{
// I can use methods from Trait1 and Trait2 here, works fine
}
}
Trait1
namespace App\Http\Traits;
trait Trait1 {
exampleMethodTrait1()
{
}
}
Trait2
namespace App\Http\Traits;
trait Trait2 {
$test = $this->exampleMethodTrait1();
}
Calling a method defined in Trait1 from Trait2 actually works, while I have not added use App\Http\Traits\Trait1; in Trait2. Is that because they are both loaded in the controller?
Okay, Let me put same code and explain you why it is working.
Trait1
<?php
namespace App\Http\Traits;
trait Trait1 {
public function exampleMethodTrait1()
{
echo 'okay';
}
}
?>
Trait 2
<?php
namespace App\Http\Traits;
trait Trait2 {
public function bar() {
var_dump(get_class($this));
$test = $this->exampleMethodTrait1();
}
}
?>
MyController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Traits\Trait1;
use App\Http\Traits\Trait2;
class MyController extends Controller
{
use Trait1, Trait2;
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$this->bar();
}
}
Now, if you will notice in Trait 2, var_dump(get_class($this)); $this is instance of MyController and not instance of trait 2, that is how it is working and it is expected behavior.
Now if you want to know if you can use one trait in side another
YES
You can do like
TaraitA
Trait A {
}
TraitB
Trait B {
use A;
}
And it will work fine.
Yes, they are both loaded in your controller as a part of it therefore they have access between them also controller methods
See the example 4
https://www.php.net/manual/en/language.oop5.traits.php
Regards
I think your confusion comes from believing that the $this inside a trait corresponds to the trait itself. But it is not.
Traits are nothing by themselves: they exists only in the context of a real class, as a helper to copy-paste methods around but not visually polluting your actual classes.
The $this you use to call exampleMethodTrait1 is not an instance of Trait2 (nor Trait1) but an instance of ExampleController, that has copied the methods over from the traits.
This doesn't happen only with traits, though, but also with parent classes in the hierarchy:
Example
abstract class Base {} // First level of inheritance
class Building extends Base {} // Second level of inheritance
class House extends Building {} // Last level of inheritance
$this (and static) always corresponds to an instance of the most concrete class of the hierarchy (the last level of inheritance).
self instead refers to the actual class instance (the same level of inheritance where the method is defined). Still never a trait, they cannot be instantiated by themselves.
the traits are not part of the hierarchy, but blindly pasted where you use them.
I currently have an abstract class which i am extending to other controllers. I have a abstract function within the abstract class which takes the value and places it in the __construct.
abstract class Controller extends BaseController {
abstract public function something();
public function __construct(Request $request) {
if (!is_null($this->something())){
$this->global_constructor_usse = $this->something();
}
}
}
My problem is that, on controllers that don't require this abstract function, I am having to place in the empty function.
class ControllerExample extends Controller {
public function something(){
return 'somethinghere';
}
}
Is there anyway to making the abstract function optional, or have a default value?
class EmptyControllerExample extends Controller {
public function something(){}
}
It is not possible to have a abstract method optional, as it is implied in PHP that all abstract methods must have an implementation.
There are legit use cases for optional abstract methods, yes: event handlers, metadata describers, etc. Unfortunately, you'll need to use regular, non-abstract methods with an empty body, and indicate in PHPDoc that they will do nothing unless extended.
Be wary, though: this can very quickly turn into code smell by diffusing a class responsability with their children. If you're dealing with generic events, you can look into Laravel's own event system, or the Observer pattern instead.
Abstract functions in a parent class, should only be used if its required by your application to implement the following method in all controllers who inherits from it, clearly it is not the case.
In this case i would make a trait. Here you create a trait which can be implemented by the classes who needs it. Notice the use keyword usage, use somethingTrait;
trait SomethingTrait
{
public function something()
{
echo "something called";
}
}
class Controller
{
use SomethingTrait;
public function run()
{
$this->something();
}
}
phpfiddle link
Another aproach could be doing a class inheritance structure, if the controllers you want to implement the methods has something in common. Where you would implement your special method in CrmController, where you still would be able to create shared methods in the abstract controller.
AbstractController
|
CrmController
|
CompanyController
For your question, 'Is there anyway to making the abstract function optional or have a default value?' No, and you are down the wrong path if you are trying to make abstract function optional. Hope my suggestions can help.
Is there any clear difference why to use abstract for extends if we can do same in with the normal class excepts it doesnt provide the contract for eg.
abstract class Survivalneeds {
abstract public function eat(); // everyone eats but different foods which would probably work as contract
public function breathe() {
// everyone inhale o2 exhale co2 only for animals
}
}
Now
class human extends Survivalneeds {
protected function eat() {
//sometimes eat goat
// contract
}
breathe()// already extending having same functionality inhale o2 and exhale co2
}
class goat extends Survivalneeds{
protected function eat() {
//wee eat greens
// contract
}
breathe()// already extending having same functionality inhale o2 and exhale co2
}
Now the same functionality can be granted by normal class by extending except the contract method and for contract we could use interface also.
What you are saying its correct inheritance works in both cases but the idea of an Abstract class is that its some common logic shared by x classes that extend this functionality but that is not instantiable by it self because it doesn't make sense (maybe you want only to have types of cars in your system but not a generic car that doesn't have a brand)
Also if you will use regular class and interface you will be forced to create stub in a class in order to follow the contract. So you will be able to create the instance of the class. And just imagine you will use this common function in your upper class.
interface Crashable{
function crash();
}
class Car implements Crashable{
function crash(){}
function getCrashParams(){
return $this->crash();
}
}
class Volvo extends Car{
function crash(){
parent::crash(); // will be OK that it's not right
//.. specific params
return $params;
}
}
class Saab{
function crash(){
//.. specific params
return $params;
}
}
$car = new Car(); // will be ok, that it's not right
//getCrashParams() function in a Car will use the local version of the crash() and not the function of it's child that will kill the data flow
You should use an interface whenever you have a need for a contract. You should use abstract class in case there's a common functionality for some simmilar classes and you don't want to repeat the code (DRY :). Of course, it is always better to use composition, but this is not the time for this discussion :)
The problem with your code (with Survivalneeds class) is the fact the class from one side is responsible for the contract (breathe and eat methods) and from another is responsible for providing common functionality. You could change your code in following way:
interface Survivor {
public function eat();
public function breathe();
}
abstract class Survivalneeds implements Survivor {
public function breathe() {
// method's body
}
}
With such implementation responsibilities are splitted. Also it is clear that all classes that will extend Survivalneeds will need to as well fulfill Survivor contract.
Can anyone tell me if it's possible to override a use statement?
My example is having an MVC setup where there is core code with the ability to override each Controller / Model with a custom version that extends the core version.
The issue I face is that my core controller has a use statement telling it to use the core model, so if I extend the model, I'm not sure how to tell it to use the custom model rather than the core one
I could obviously update the core controller use statement to point to the custom one, but the core code is shared so the custom version may not exist on other sites that use this core core
Use statements are obviously file level so I'm guessing it's not possible, but I'm hoping there's either something I don't know about or maybe a workaround
Example
Core controller
namespace Core;
use Core\Model\Example as ExampleModel;
class ExampleController {
public function output() {
$model = new ExampleModel;
$model->test();
}
}
Core Model
namespace Core;
class ExampleModel() {
public function test() {
echo 'This is the core test';
}
}
Custom Controller
namespace Custom;
use Custom\Controller\Example as Base,
Custom\Model\Example as ExampleModel;
class ExampleController extends Base {
//Inherits the output() method
}
Custom Model
namespace Custom;
use Core\Model\Example as Base;
class ExampleModel extends Base {
public function test() {
echo 'This is the custom test';
}
}
So given this example, is it possible for me to create an instance of the custom controller which uses the custom model to output 'This is the custom test', without modifying the core code at all?
Hopefully what I'm asking makes sense
Thanks
I'm not quite sure I understand your question, but the answer should be self-evident: If your custom model extends from the core model, you can simply extend another class from that custom class
If you are writing code, that depends on a child of the core class being present, then that child class becomes a vital part of your project. If you can't change the core itself, add that class as a dependency. It's as simple as that.
Adding a second layer of inheritance needn't worry you, it's perfectly common to do so. Something like this is perfectly predictable, and reliable:
namespace Core;
class Model
{
public function coreTest()
{
return 'from the core';
}
}
namespace Custom;
use Core\Model;
class CustomModel extends Model
{
public function customTest()
{
return 'from the custom model';
}
}
//finally
namespace Project;
use Custom\CustomModel;
class ProjectModel extends CustomModel
{
public function test()
{
return array(
$this->coreTest(),
$this->customTest(),
'From the project'
);
}
}
$test = new ProjectModel();
echo implode(PHP_EOL, $test->test());
If, however you want a given class to extend from another class, based on whether or not that class exists, you are looking for conditional imports.
A simple use statement is evaluated at compile-time, so there's no way you can use an if check to switch between which class you extend from.
There is, however a hacky work-around, but I wouldn't rely on it. Check if the given class exists (without autoloading), and set an alias to the class that does.
if (!class_exists('\\Custom\\Model', false))
class_alias('\\Core\\Model', 'Base');
else
class_alias('\\Custom\\Model', 'Base');
class CustomModel extends Base
{}
But really: don't go down this route. Sure your code will work, but if you then rely on a method being available, that was defined in the custom class, but that class was missing, then your code will fail... horribly.
Details on conditional imports:
Why use class alisases?
I have a few standard classes:
abstract class Parent {}
class Child1 extends Parent {}
class Child2 extends Parent {}
The Parent class contains logic common to both child classes, but each child class has its own additional logic.
For each of my clients, this logic can be configurable. So for any particular client, I may have:
abstract class ClientParent {}
class ClientChild1 extends ClientParent {}
class ClientChild2 extends ClientParent {}
The problem I'm having is how to get the logic from the standard classes into these ones. The first approach would be something like this:
abstract class ClientParent extends Parent {}
Okay, now I have the standard parent logic in all my client-specific classes. Great. But the child classes are already extending ClientParent, so we can't do the same thing for them. My "solution" is then to do this:
abstract class Parent {}
class Child1 extends ClientParent {}
class Child2 extends ClientParent {}
abstract class ClientParent extends Parent {}
class ClientChild1 extends Child1 {}
class ClientChild2 extends Child2 {}
There; now all the appropriate logic is passed down and everybody's happy. Except that now my standard classes are coupled to a particular client. As I have many clients, this is obviously no good.
What's my out here? Is there a way to address this through inheritance alone, or should I look into more complex configuration injection strategies?
Edit:
I'm using PHP 5.3, so I am unable to use traits to solve this problem.
PHP does not support multiple inheritance which is what I think you are trying to approximate here. It does however support (as of 5.4) traits, which in many cases can provide you with comparable functionality.
trait ParentTrait {
public function someUsefulMethod(){/*...*/};
public function someOtherUsefulMethod(){/*...*/};
}
abstract class ClientParent(){}
class ClientChild1 extends ClientParent {
use ParentTrait;
}
$clientChild1 = new ClientChild1();
$clientChild1->someUsefulMethod();
Another option would be to use composition instead, possibly for your problem employing the Strategy pattern would work.
class SuperWidget extends Widget{
private $dataStrategy;
public function __construct(DataStrategy $strategy){
$this->dataStrategy = $strategy;
}
// do this if you need to expose the functionality.
public function getData(){
return $this->dataStrategy->getData();
}
// or if you are just using it in your class
public function renderWidget($option){
$data = $this->dataStrategy->getData($option);
// use the data to render the widget;
return $renderedWidget;
}
}
$dataStrategy = JsonDataStrategy("http://data.source.url/jsonService.php");
$widget = new SuperWidget($dataStrategy);