I have created a class that extends Array. I want to execute arbitrary code before calling the inherited push function.
class newArray extends Array{
//execute any logic require before pushing value onto array
this.push(value)
}
The solution I found was to create a new function in the subclass that has the same name as the inherited function. In this case push. Then inside the overriding function the inherited function is called via the super keyword.
class newArray extends Array{
push(value) {
//execute any logic require before pushing value onto array
console.log(`pushed ${value} on to array`)
super.push(value)
}
}
var array = new newArray
array.push('new Value')
Related
Im trying to load my model in my controller and tried this:
return Post::getAll();
got the error Non-static method Post::getAll() should not be called statically, assuming $this from incompatible context
The function in the model looks like this:
public function getAll()
{
return $posts = $this->all()->take(2)->get();
}
What's the correct way to load the model in a controller and then return it's contents?
You defined your method as non-static and you are trying to invoke it as static. That said...
1.if you want to invoke a static method, you should use the :: and define your method as static.
// Defining a static method in a Foo class.
public static function getAll() { /* code */ }
// Invoking that static method
Foo::getAll();
2.otherwise, if you want to invoke an instance method you should instance your class, use ->.
// Defining a non-static method in a Foo class.
public function getAll() { /* code */ }
// Invoking that non-static method.
$foo = new Foo();
$foo->getAll();
Note: In Laravel, almost all Eloquent methods return an instance of your model, allowing you to chain methods as shown below:
$foos = Foo::all()->take(10)->get();
In that code we are statically calling the all method via Facade. After that, all other methods are being called as instance methods.
Why not try adding Scope? Scope is a very good feature of Eloquent.
class User extends Eloquent {
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
public function scopeWomen($query)
{
return $query->whereGender('W');
}
}
$users = User::popular()->women()->orderBy('created_at')->get();
Eloquent #scopes in Laravel Docs
TL;DR. You can get around this by expressing your queries as MyModel::query()->find(10); instead of MyModel::find(10);.
To the best of my knowledge, starting PhpStorm 2017.2 code inspection fails for methods such as MyModel::where(), MyModel::find(), etc (check this thread), and this could get quite annoying.
One (elegant) way to get around this is to explicitly call ::query() wherever it makes sense to. This will let you benefit from free auto-completion and a nice formatting/indentating for your queries.
Examples
BAD
Snippet where inspection complains about static method calls
// static call complaint
$myModel = MyModel::find(10);
// another poorly formatted query with code inspection complaints
$myFilteredModels = MyModel::where('is_foo', true)
->where('is_bar', false)
->get();
GOOD
Well formatted code with no complaints
// no complaint
$myModel = MyModel::query()->find(10);
// a nicely formatted and indented query with no complaints
$myFilteredModels = MyModel::query()
->where('is_foo', true)
->where('is_bar', false)
->get();
Just in case this helps someone, I was getting this error because I completely missed the stated fact that the scope prefix must not be used when calling a local scope. So if you defined a local scope in your model like this:
public function scopeRecentFirst($query)
{
return $query->orderBy('updated_at', 'desc');
}
You should call it like:
$CurrentUsers = \App\Models\Users::recentFirst()->get();
Note that the prefix scope is not present in the call.
Solution to the original question
You called a non-static method statically. To make a public function static in the model, would look like this:
public static function {
}
In General:
Post::get()
In this particular instance:
Post::take(2)->get()
One thing to be careful of, when defining relationships and scope, that I had an issue with that caused a 'non-static method should not be called statically' error is when they are named the same, for example:
public function category(){
return $this->belongsTo('App\Category');
}
public function scopeCategory(){
return $query->where('category', 1);
}
When I do the following, I get the non-static error:
Event::category()->get();
The issue, is that Laravel is using my relationship method called category, rather than my category scope (scopeCategory). This can be resolved by renaming the scope or the relationship. I chose to rename the relationship:
public function cat(){
return $this->belongsTo('App\Category', 'category_id');
}
Please observe that I defined the foreign key (category_id) because otherwise Laravel would have looked for cat_id instead, and it wouldn't have found it, as I had defined it as category_id in the database.
You can give like this
public static function getAll()
{
return $posts = $this->all()->take(2)->get();
}
And when you call statically inside your controller function also..
I've literally just arrived at the answer in my case.
I'm creating a system that has implemented a create method, so I was getting this actual error because I was accessing the overridden version not the one from Eloquent.
Hope that help?
Check if you do not have declared the method getAll() in the model. That causes the controller to think that you are calling a non-static method.
For use the syntax like return Post::getAll(); you should have a magic function __callStatic in your class where handle all static calls:
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
For sure this kind of stuff could be implemented in some other way but I'm still curious why TS doesn't warn. Same structures could have different data.
class ComponentContainer<TComponent> extends Array<TComponent> {}
class Context<TComponent> extends Array<ComponentContainer<TComponent>> {}
///////////////////////////////////////////////////////
class Component<T> { constructor(public value: T) {} }
class Movable extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
class Static extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
///////////////////////////////////////////////////////
const ctx: Context<Movable> = new Context()
ctx[0].push(new Static()) // <-- No error
EDIT
Could be a beginning of an alternative here :https://basarat.gitbooks.io/typescript/docs/tips/nominalTyping.html
Types don't presume that value should be an instance of specified class. Movable class is used as interface here:
const ctx: Context<Movable> = new Context()
If a value should be an instance of Movable class, this check is usually performed at run time with if (value instanceof Movable). It will be tricky to implement in Array subclass, it may be better for Context to implement its own API that allows to validate values instead of extending Array.
Same structures could have different data
But they don't have different data. Static conforms to Movable interface, so it doesn't cause type error. Similarly, this will work, because the object conforms to this interface:
ctx[0].push({ value: true })
And will cause TypeScript type error if it doesn't:
ctx[0].push({ value: 1 })
It's possible to additionally enforce the value to be an instance of Movable through type checks with merged interface:
interface Movable {
constructor: typeof Movable
}
class Movable extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
It still will be possible to cheat typing system with:
ctx[0].push({ constructor: Movable, value: 1 });
After playing a bit with your code in TypeScript Playground, it seems like TypeScript is smart enough to detect that Movable and Static are basically the same type besides their name. They are both a Component<boolean> and have no properties, so it allows you to push a new Static instance into a Movable array. Only when I added a property to Movable which didn't exist in Static then TypeScript compiler showed an error, for example:
I guess that's just how TypeScript works. It doesn't necessarily prohibit you from using different types on a generic object unless the type that's given is missing properties which exist on the expected type. That's why the following also works:
I am getting this error when I put it in the class controller. It works fine if I remove the class controller. How do I fix it?
Parse error: syntax error, unexpected 'require_once' (T_REQUIRE_ONCE),
expecting function (T_FUNCTION) in
D:\xampp\htdocs\m2302\admin\controller\tool\import_export.php on line
9 expecting function
class ControllerToolImportExport extends Controller {
//$dir = (strcmp(VERSION,'3.0.0.0')>=0) ? 'library/export_import' : 'spreadsheet';
//chdir( DIR_SYSTEM.$dir );
//require_once('src/PhpSpreadsheet/Spreadsheet.php');
*require_once('D:/xampp/htdocs/m2302/system/spreadsheet/autoload.php');*
//include the classes needed to create and write .xlsx file
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
//object of the Spreadsheet class to create the excel data
$spreadsheet = new Spreadsheet();
//add some data in excel cells
$spreadsheet->setActiveSheetIndex(0)
->setCellValue('A1', 'Domain')
->setCellValue('B1', 'Category')
->setCellValue('C1', 'Nr. Pages');
You code is all just "loose" in the class.
It should be in a method (function) instead e.g.:
class ControllerToolImportExport extends Controller {
function index() {
// Put your code here.
}
}
Method
method() {}
function
function func() {}
Above is just to elaborate difference between method and function.
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
method1(){}
}
In the above class, after writing the definition.
I want to add a method2 to the class, similar to the way method1 is there.
I can add a function like soo
Student.prototype.func = function(){...}
But I do not have a way to add a method on the same class. and inside function I will not be able to use super as that is just available inside the method.
Is there a way I can add method after the class is defined ?
So that I will be able to use super inside that.
As has already been explained, you can only use super() inside the regular class definition. But, long before we had ES6, we were calling parent method implementations manually. It can be done using the parent's prototype:
class Person {
talk() {
// some implementation here
}
}
class Student extends Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Student.prototype.talk = function(data) {
// now call base method manually
Person.prototype.talk.call(this, data);
// then do our extra work
log(data);
}
Of course, normally you could just declare all your methods within the class declaration so this would not be something you would normally need to do.
Your snippet adding a new property to the prototype is only approach for adding a function later. One main difference in this case is that simple assignment like that will create the property as enumerable by default, whereas class syntax would create is as non-enumerable. You could use
Object.defineProperty(Student.prototype, "func", {
configurable: true,
writable: true,
value: function() {
},
});
to address that at least.
Unfortunately as you've seen, adding things to the prototype afterward does not allow usage of super.foo. There is no way for this to be supported, because the behavior of super is based specifically on the lexical nesting of the method syntax method(){} being inside of the class syntax. Methods added programmatically later on would have no way to know which prototype is the "super" one.
From a string I'm trying to instantiate a class (my marionette view). I found a way that works but this way has a problem where I can't actually pass a parameter to the instantiated class.
It seems when I call typeMapping[viewType] it's actually returning me Show.OneNode() instead of just Show.OneNode
class Show.TwoNode extends App.ItemView
template: "templates/two"
class Show.OneNode extends App.ItemView
template: "templates/one"
class Show.Layout extends App.Layout
onShow: =>
typeMapping = {
one: Show.OneNode
two: Show.TwoNode
}
viewType = "one"
view = new typeMapping[viewType]
model: #model
again, I would have rather made this a comment, but hey that's life. Have you tried wrapping your values from your key/value pairs in quotes to force them as strings?
typeMapping = {
one: "Show.OneNode",
two: "Show.TwoNode"
}