I'm trying to understand how to use USE in base and extended classes. I have searched around but I don't think I have the correct terminology.
Let's say my base class looks like
namespace App\Classes;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
class ExcelReport
{
public $spreadsheet;
public function __construct()
{
$this->spreadsheet = null;
}
}
and then I extended that class
namespace App\Classes;
class MonthlyExcelReport extends ExcelReport
{
public $id;
public function __construct(int $id)
{
parent::__construct();
$this->id = $id;
}
public function build()
{
$reader = IOFactory::createReader('Xlsx');
}
}
What do I have to do to get the call to IOFactory in the extended class to recognize that use PhpOffice\PhpSpreadsheet\IOFactory; is present in the base class?
I currently get this error Class 'App\Classes\Gap\IOFactory' not found and I don't want to have to repeat all of those use statements in the extended class.
TL;DR;
Namespace is there to allow you to have two classes named the same, but in a different namespace.
Imagine the class Animal\Bear\Claw and Machinery\Compactor\Claw, are things possible with namespaces, when we needed ugly class names like Animal_Bear_Claw and Machinery_Compactor_Claw before the introduction of namespaces in PHP.
Now when you instanciate or use those classes, you don't want to allways have to go in the extends and say
new \Animal\Bear\Claw();
You want to be able to say: "I am in the context of an Animal Factory Pattern and will basically act upon the classes under the Animal namespace, not the Machinery's ones"
So you go:
use Animal\Bear\Claw;
new Claw();
Or
use Animal\Bear;
new Bear\Claw();
Or even, with aliases
use Animal\Bear as MyTeddyBear;
new MyTeddyBear\Claw();
And so, inheriting another class from another containing uses, just does nothing, you'll have to repeat your uses, maybe simplifying them, and most likely, not add uses for class you actually do not use in the said class (did you know that good IDE does prompt you about unused use statement present in your classes and help you add the good ones to your use statements?).
The use statement are not like an include like you seems to believe it.
It is just there to say: "thanks to namespaces, I can have multiple classes with the same name, now the class I want to use is actually under the namespace defined by use"
You are not forced to state a full namespace either in your use statements.
For example:
namespace App\Classes;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class MonthlyExcelReport extends ExcelReport
{
public function __construct()
{
$reader = IOFactory::createReader('Xlsx');
$workSheet = new Worksheet();
}
}
Could be shortened this way:
namespace App\Classes;
use PhpOffice\PhpSpreadsheet; // This means "all the classes that I am going to use, if not in the same namespace as the current class (App\Classes) would come from the namespace PhpOffice\PhpSpreadsheet"
class MonthlyExcelReport extends ExcelReport
{
public function __construct()
{
$reader = PhpSpreadsheet\IOFactory::createReader('Xlsx');
$workSheet = new PhpSpreadsheet\Worksheet\Worksheet();
}
}
Further reading:
https://www.php.net/manual/en/language.namespaces.faq.php
https://www.php.net/manual/en/language.namespaces.rules.php
Use operator is used to "include" a class.
If you don't use "Use" operator, than you can include as "full path".
In your case:
$reader = IOFactory::createReader('Xlsx');
Should be:
$reader = PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
Related
I am trying to separate functionalities into two classes and I want to Inject one class into another. However, it seems that Laravel can't recognize the second class and it is always null.
namespace App\Services;
use App\Models\Image;
use App\Models\Offer;
class ImagesService {
public function __construct() {
}
function saveImages(iterable $images, Offer $offer): array {
// ... code here !
}
}
And the class to inject in is:
namespace App\Services;
use App\Models\Action;
use App\Models\Offer;
use App\Models\OfferOption;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class OffersService {
protected $imagesService;
function __constructor(ImagesService $imgService) {
$this->imagesService = $imgService; //Doesn't work ! $imagesService is always null!
}
function doSomething() {
$this->imagesService->saveImages(....) // Call to a member function saveImages() on null at
}
}
there are several ways to access and use properties from one class to another class.
Create an instance of the class you want to use in the class you want to use.
Use the class you want to use as Trait and call it use in the other class.
Extend the class you want to use to the class you want to use.
I'm currently working on a PHP trait thay will help me to reuse code in some class controllers that I have using Laravel framework.
I wanted to make the trait methods as dynamic as I could but when trying to access to a class that my parent class imported, I get a Class not found exception.
My class controller is as follows:
namespace App\Http\Controllers\Admin;
use App\Models\ {
Curso,
Leccion,
Diapositiva,
ImagenDiapositiva
};
use App\Traits\TestTrait;
class DiapositivasController extends Controller{
use TestTrait;
public function addRecord(Request $request){
$request->class_name = 'ImagenDiapositiva';
$this->addImage($request);
}
}
My Trait:
namespace App\Traits;
trait TestTrait{
public function addImage($request){
$class_name = $request->class_name;
$diapositiva = new $class_name;
//extra code
}
}
So my doubt is, do I have to include the model classes I want to use inside my Trait again or am I doing something else wrong?
if you use new with a variable class name, you have to use the fully qualified class name. I'm guessing new $class_name is the root cause of the issue here, since $class_name would have to be something like: 'App\Models\ImagenDiapositiva' or whatever the full namespace is. Just have to change the call $request->class_name = 'ImagenDiapositiva'; to reflect the full name of the class.
I am converting from c# to php and I'm having trouble transitioning in some places. Particularly namespaces. The problem I am having is I have to fully qualify every namespace when calling a class from another namespace. Is this normal?
<?php
namespace Lib\Things;
class TheThings
{
}
then in the other class
<?php
namespace App;
use Lib\Things;
class DoStuff
{
public function doStuff()
{
$things = new TheThings();
}
}
That doesn't work... I end up having to do
new Lib\Things\TheThings();
Or
<?php
namespace App;
use Lib\Things as T;
class DoStuff
{
public function doStuff()
{
$things = new T\TheThings();
}
}
I've also got this in my composer.json file
"psr-4": {
"App\\": "app/",
"Lib\\": "lib/"
}
Is that normal or am I doing something wrong here?
In the PHP manual the use keyword is referred to as importing or aliasing.
This means that
use Lib\Things;
and
use Lib\Things as Things;
are the same. This results that you don't have to use the fully qualified names to instantiate classes from a namespace, you can use only the alias of the imported namespace. So in your case, the following would have worked:
<?php
namespace App;
use Lib\Things;
// same as:
// use Lib\Things as Things; // explicit alias
class DoStuff
{
public function doStuff()
{
$things = new Things\TheThings();
}
}
(Note that this is the same as your second example, the only difference is that the alias is not explicitly set here to T (and defaults to Things).
To be able to use the class name without any namespace prefix, you will have to set the alias of the actual class:
<?php
namespace App;
use Lib\Things\TheThings;
// same as:
// use Lib\Things\TheThings as TheThings; // explicit alias
class DoStuff
{
public function doStuff()
{
$things = new TheThings();
}
}
In conclusion, if you start thinking about the use operator as setting an alias for a namespace or class (or other), you will get the hang of it.
PS 1:
Before PHP 7, if you wanted to import multiple classes from the same namespace, you had to write:
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;
From PHP 7.0 onwards, classes, functions and constants being imported from the same namespace can be grouped together in a single use statement:
use some\namespace\{ClassA, ClassB, ClassC as C};
PS 2:
composer helps in automagically including/loading the actual php files, based on some PSR* rule, it does not have any role in how namespaces work in bare PHP.
When you do:
$things = new TheThings();
Class is searched on the current namespace (App in your example), thats why not worked.
So, you need to specify full class namespace, so interpreter know which class are you refering, you could also have a namepsace2/TheThings and this/is/another/namespace/TheThings classes.
Just include full class namespace
use Lib\Things\TheThings;
Have encountered an issue I can't seem to figure out now by myself.
Using Symfony autoload module.
Here's my factory:
namespace Core\Factories;
use \Core\Gateway;
class DatabaseAccessFactory {
// Define client type
const DEF = 'mongo';
public function createObject($type) {
switch($type) {
case self::DEF:
return new MongoClientGateway();
break;
default:
return false;
}
}
}
Example of /Core/Gateway/MongoClientGateway.php
<? namespace Core\Gateway;
class MongoClientGateway implements MongoDbGateway {
public function setUp(){
}
public function query(){
}
public function save(){
}
}
So, basically I'm using "use" keyword to load gateway namespace into my current one, and then I try to instantiate a class that is under \Core\Gateway namespace, but it says class is not found. Am I missing something?
You need to specifcy the class as well
use Core\Gateway\MongoClientGateway
or access the class with the namespace you used
new Gateway\MongoClientGateway
Btw, there's no need for the first "\" in use \Core\Gateway
It's use Foo\Bar, without leading backslash.
use Foo\Bar does not mean that every Class implicitly resolves to Foo\Bar\Class now. use Foo\Bar is shorthand for use Foo\Bar as Bar, so you can reference the namespace Foo\Bar using merely Bar. use is not "importing a namespace", it's aliasing a namespace to a shorter name.
Therefore you need to write Gateway\MongoClientGateway, or use Core\Gateway\MongoClientGateway explicitly if you want to be able to write just MongoClientGateway.
you used "use" wrong.
waht "use" does, is to tell your code where class comes from.
sample code:
use \my\namespace\className
new ClassName();
this will make the className accassible without a namespace.
My current code is as following:
namespace Libraries;
class_alias('Libraries\ORM', 'ORM');
class ORM
{
public function __construct() {}
static public function someMethod()
{
// do something
}
}
I thought I could shortcut the namespace as you can see above, so I only needed to call the ORM::someMethod(); instead of \Libraries\ORM::someMethod();
(I am using the ORM class in another namespace, lets says 'Project')
Is this possible or what is the right solution?
I know that I could store the class in a global namespace, but then I still need to use the global slash like: \ORM::someMethod();.
Thanks!
Simply alias the classname when you are importing it:
namespace SomethingEntirelyDifferent;
use Libraries\ORM as ORM;
ORM::someMethod();