Short version of the question is:
Is there a way to send the actual $object to the custom directive and reuse it in a view?
Details:
I created a new directive, which is:
#object_actions($object)
This $object is actually an object, and it can be named anything. The directive returns a view with html code, built as a tool to be used everywhere on the site.
So let's say I have a $post. That post can have $post->like(). It is basically a model, now just like it can be $post it can be $photo as well, both are models.
Now let's say I am in the photos view, with that #object_actions I can easily embed the layouts.objects.actions.blade.php views like that:
#object_actions($photo)
Same in posts views, I can
#object_actions($post)
Both are models again, or they can be anything object with methods, etc.
I could simply use #include('layouts.objects.actions') and it works. But that's not practical at all, since the layouts.objects.actions has this html code:
<span data-liked="<?php echo $object->isLiked() ? 'true' : 'false';?>">
<span class="likes-count">{{$object->likes()->count()}}</span>
</span>
So as you can see, it uses $object as the var name or object name, including this in posts view, or any view where the actual var name is either $post, $photo, $comment is never $object, and doing something like $object = $post, $object = $photo is kind of messy.
I thought I could create a directive like
Blade::directive('object_actions', function ($object) {
// dd($object) -- returns a string and not the model
// Include the view here
});
However, $object is always a $string, a dd before the return dd($object) returns a string, therefore the actions view throws an error call to a member function xxx() on string.
dd on
#object_actions($photo) // returns '$photo
#object_actions($post) // returns '$post'
I want the real object I sent from the original view.
So is there a way to send the actual $object to the custom directive and reuse it in a view?
The solution I found was this:
// AppServiceProvider
Blade::directive('actions', function ($object) {
return "<?php echo \$__env->make('blocks.objects.actions', ['object'=>{$object}])->render(); ?>";
});
Related
I'm very new to Laravel and I was given a Laravel project, where I need to add some new features. The person, who has previously worked on that project hadn't left even a single comment in the code and now I must make my own scenarios about the features.
I have a controller, defined with some functions (dashboard, show_project, save_project etc.) and in one of my function, I need to use the result of calling other function.
In the concrete example, the call is made from "http://127.0.0.1:8000/username/project_slug" - there is a button "Save" and post function, called on onClick event. The function, whose output I need is normally called on "http://127.0.0.1:8000/username/project_slug/svg", which returns a view.
For better understanding, there's an example of the flow:
The user wants to save his/her project (an UML diagram) but in order to have a thumbnail, a function which generates a view (SVG format) will be called and the idea is, to take the HTML content of the page, which is on "http://127.0.0.1:8000/username/project_slug/svg" and to pass it to another API in order an image to be generated.
So far, I tried with cURL, file_get_contents, file_get_html, render methods but when I return the output, the server just keeps waiting and shows no error messages.
//The both functions are in ProjectController.php
/**
* A function, for saving the json file, where the whole of the diagram
* components are described. From the frontend we receive the project_id and
* the project_data(the json content).
*/
public function save_project(Request $request) {
$input = $request->only(['project_id', 'project_data']);
/*
Here we need to call the other function, to render the HTML content
and to pass it to the other API. Then we save the result with the
other information.
*/
/*
What I've tried?
$new_link = 'http://' . $_SERVER['HTTP_HOST'] . "/$username"
."/$project_slug" . "/svg";
$contents = file_get_contents($new_link);
return $contents;
*/
//In the same way with cURL.
$project = Project::where('user_id',session('userid'))
->where('id',$input['project_id'])->first();
$project->project_data = json_encode($input['project_data']);
if($project->save()) {
return ["status"=>"saved"];
}
else {
return ["status"=>"error"];
}
}
/**
* A function, which takes the the Json content (project_data) from the
* database and passes it to the view, where the Json is transformed in HTML
* tags.
*/
public function generate_svg(Request $request,$username,$project_slug) {
if(session('username')!=$username) {
return redirect("/");
}
$userid = session('userid');
$project = Project::where([
'user_id' => $userid,
'slug' => $project_slug,
])->first();
if(!is_null($project)) {
return view('svg',compact('project'));
}
}
I've read about some possible ways, including Guzzle request but maybe I haven't understood correctly the idea:
If I need to make a Guzzle request from my controller to the other function inside my controller, do I need an API configuration?
What I mean? Example:
Before saving the project, the user is on this URL address "http://127.0.0.1:8000/hristo/16test". Inside the controller, I have in session variables the token, the username(hristo) and i can get the project_name(16test) from the URL but after passing this URL to the generate_svg function, there is no indication of error or success.
So I'm missing some kind of token information?
If you just need the response of the other function you can just use
$response = $this->generate_svg($request, $username, $project_slug);
If you'll need to use this function from a different controller you can use this
app('App\Http\Controllers\UsernameController')->generate_svg($request, $username, $project_slug);
I need your help.
I've just found out that the class \Illuminate\View\Environment has a protected array $sections and a method to get it getSections() but it returns an empty array.
Here's how I tried to get it:
$view = View::make('pages');
$env = $view->getEnvironment();
$env->make('pages');
print($env->getSections());
And the result is Array( )
Where did I go wrong? Any suggestion will be appreciated.
That's because when you call $env->make('pages'), the view isn't immediately processed. It's only processed when the render method on the $view object is called, which is done automatically when you return it as a response, via the __toString() method. The problem, though is that just after the page is rendered, still inside the render method, the $sections variable is cleared, using the flushSections method. So you actually never have access to it.
You could try to fool it if you call the incrementRender method before making the view, then make and render the view, get the sections and then finally decrementRender() and flushSections(), but this could bring unexpected results. Something like this:
// Fool it
$env->incrementRender();
$env->make('pages')->render();
$sections = $env->getSections();
// Clear it
$env->decrementRender();
$env->flushSections();
Need advise, as was getting "Undefined variable: tpl in /home/mytoys11/public_html/components/com_forms/controller.php on line 101"
function toys(){
// Create the view
global $Itemid;
$model = & $this->getModel('pages');
$view = & $this->getView('pages', 'html');
$view->setLayout('toys');
// Push the model into the view (as default)
$view->setModel($model, true);
// Display the view
$view->toys($tpl);
}
Which is solved like this by removing the undefined variable $tpl from view in the last line
function toys(){
// Create the view
global $Itemid;
$model = & $this->getModel('pages');
$view = & $this->getView('pages', 'html');
$view->setLayout('toys');
// Push the model into the view (as default)
$view->setModel($model, true);
// Display the view
$view->toys();
}
The page is loading fine after removing $tpl. I think tpl is empty string, but is this the correct way or the function is poorly optimized, any suggestions. Thanks
Edit
Thanks, As advised, here's the code been modified
public function toys(){
$model = $this->getModel('pages');
$view = $this->getView('pages', 'html');
$view->setLayout('toys');
$view->setModel($model, true);
$view->toys();
}
However, it does not work with using function name as :-
displaytoys()
It is ok and safe to omit the $tpl argument, if you don't want to address a specific (sub-) template of your view.
The code has several other problems, though.
Visibility is not declared. For an action in a controller this should be public.
Method names are verbs, not nouns.
Never use global. $Itemid is not even used.
Don't comment the obvious facts.
PHP4 is gone, so objects are assigned by reference by default.
So your code should look like this:
public function displayToys()
{
$model = $this->getModel('pages');
$view = $this->getView('pages', 'html');
$view->setLayout('toys');
$view->setModel($model, true);
$view->displayToys();
}
In order to make the renaming to displayToys work, you'll also have to change other places in your code. Wherever you refer to the task toys, you have to change it to displayToys. The corresponding method in the view class has to be renamed, too.
Since this only is a style issue, it is ok to leave the name alone and stay with toys in the first step. You'll not get functional problems from that.
This is probably very easy to do, but I can't seem to get my head around it right now. Let's say in a component in a cakephp application, I have a variable my_model, which contains the model of the corresponding controller that is currently using the component like:
function TestComponent extend Object
{
var $my_model; // can be either User, or Person
function test()
{
$myModelTemp = $this->my_model;
$model = $myModelTemp != 'User' ? $myModelTemp.'->User' : 'User';
$this->$model->find('all');
}
}
As you can see above in my function test() what I'm trying to do is call the correct model based on the value of my_model. So based on the condition, my query will be either:
$this->Person->User->find('all');
Or
$this->User->find('all');
When I do it like I did above, I get an error saying Fatal error: Call to a member function find() on a non-object. In order words, that error means Person->User is not an object (so, it is considered as a string).
What you're saying could be true, however, it can refer to any part of the call.
So either Person or User could be invalid, or together they causes the error. Hard to say.
Try dumping the individual objects using var_dump();
So try:
<?php
echo "<pre>";
var_dump(is_object($this->Person));
var_dump(is_object($this->User));
echo "</pre>";
?>
to determine where you're code goes wrong.
To be clear, that return value needs to be true for it to be an object.
The one that returns false is the likely culprit.
Should your question refer to the correct way to reference an object, an object is basically an array. For example:
<?php
$obj = (object) array("this", "my_function");
?>
The above example casts the array as an object. However, using multiple layers might prove to be more difficult than you'd expect.
Generally, it looks like you might be going about this all wrong. Obviously you want the models to be dynamic, but then you're hard-coding things which defeats the whole point of it being dynamic in the first place.
It also seems like you might be violating the principals of CakePHP and MVC by doing all this in a component. I'm not sure this component should really be manipulating models or assuming which models are currently in use.
However, if you want to evaluate a string as an actual object, you can wrap it in { ... } (this is valid standard PHP syntax, not Cake-specific code).
Try this:
$modelName = $this->my_model;
$model = ($modelName != 'User') ? $this->{$modelName}->User : $this->User;
$model->find('all');
Now, if this doesn't work or you get an error saying it can't find the model(s) you need to ensure the models are actually loaded and initialised in the current scope.
I'm writing a PHP script that accesses two different data objects - one is backed by a DB, the other contains session data. They both contain the same fields - i.e. the data that is in the session object will make its way into the DB once it is validated.
I'm trying to write a function that will first check the db-backed object for a value, then check the session-data-backed object for a value. It's not working like I quite expect, however. Here is what I have.
<?php
function check_cache($field){
// $App is the DB backed object, $data is the session object
return $app->$field ? $app->$field : $data->$field ? $data->$field : '';
}
?>
I'd like to be able to call the function like this:
<input type="text" value="<?php echo check_cache('address'); ?>" />
...but the function always returns nothing. When I replace the function call with the actual inline code, substituting $field with the field name I want, it works. What am I missing?
You should ensure that $app and $data exist within the scope of the function (potential by passing them as parameters, I'll leave that to you). Try this:
<?php
function check_cache($field){
// $App is the DB backed object, $data is the session object
return $app->${$field} ? $app->${$field} : $data->${$field} ? $data->${$field} : '';
}
?>
You should probably extend this though to ensure that $app->$$field exists, etc.
Edit:
This is pretty wrong, as pointed out by bob-the-destroyer in the comments. $app->$field is all you need. Just make sure the scope is right, and the members you want aren't private. Apologies.
You can try something like this:
<?php
function check_cache($field){
global $app, $data;
// $App is the DB backed object, $data is the session object
$av = eval("\$app->".$field);
$dv = eval("\$data->".$field);
return $av ? $av : $dv ? $dv : '';
}
?>
I'd be tempted to say it's a scope issue, $app + $data don't exist inside the function, only outside. Extend the function to take two extra params, and pass in the $app + $data objects that way.
Addendum
This won't entirely fix the problem, you'll still need to access the fields of the object, depending on your class structure, if it's properties are private, will make some of the alternate answers not work, as the field won't be accessible via the obj->field syntax.