livewire "render" method must run instance of View - laravel-livewire

Hey I am trying to use livewire for the first time and I successfuly done the function and when its finish the function checkSingleLink() its shows this error :
"render" method on [App\Http\Livewire\SingleAsset] must return instance of [Illuminate\View\View]
the Livewire Model
namespace App\Http\Livewire;
use Illuminate\Http\Request;
use App\Models\Links;
use App\Models\Cron;
use Livewire\Component;
class SingleAsset extends Component
{
public $saved = False;
protected $fillable = [
'keyword', 'data', 'updated_at'
];
public function __construct() {
$this->cron = new Cron();
}
public function render(Request $request)
{
if(Links::where('asset_id', '=', $request->id)->exists()){
$links = Links::where('asset_id', '=', $request->id)->get();
return view('livewire.single-asset', compact('links'));
}
}
public function checkSingleLink(Links $link, $keyword) {
// cron::fetchUrl($link->link, $keyword);
$data = $this->cron->fetchUrl($link->link, $keyword);
$link->keyword = $keyword;
$link->data = $data;
$link->save();
// dd($this->cron->fetchUrl($link->link, $keyword));
session()->flash('message', "link checked");
$this->saved = TRUE;
}
}
this is the location where I call the function
<input class="inputs" style="border:0;" type="text" value="{{$link->keyword}}" wire:keydown.enter="checkSingleLink({{$link}}, $event.target.value)"/>
this part of the calling and making the function working but after when he need to render again the response its not working

Related

Why listing of items is empty in calling method of the component?

with livewire 2 I have listing of items ($itemDataRows var) and I need for any item show checkbox ($selectedItems var) and
"Select all" button and clicking on this button all items must be selected. I do :
class CrudItems extends Component
{
private $itemDataRows = [];
public $selectedItems = [];
...
public function render()
{
...
$this->itemDataRows = Item
::orderBy('created_at', 'desc')
...
->paginate($backend_per_page);
return view('livewire.admin.items.crud-items', [
'itemDataRows' => $this->itemDataRows,
'item_rows_count' => $this->item_rows_count
])->layout('layouts.admin');
}
}
public function calcSelectedItemsCount()
{
$ret= 0;
foreach( $this->selectedItems as $next_key=>$next_value ) {
if($next_value) {
$ret++;
}
}
return $ret;
}
public function selectAllItems()
{
$this->selectedItems= [];
\Log::info( dump($this->itemDataRows, ' -0 $this->itemDataRows selectAllItems::') );
// INL OG FILE_I SEE THAT ARRAY ABOVE IS EMPTY!!!
foreach( $this->itemDataRows as $nextItemDataRow ) {
$this->selectedItems[$nextItemDataRow->id] = true;
\Log::info( dump($this->selectedItems, ' -$this->selectedItems INSIDE selectAllItems::') );
}
\Log::info( dump($this->selectedItems, ' -$this->selectedItems selectAllItems::') );
}
and in template :
$selectedItems::{{ var_dump($selectedItems) }}<hr>
$itemDataRows::{{ $itemDataRows }}
/* $selectedItems is filled ok when I click on checkboxes , but $itemDataRows shows empty var, though I filled items listing below */
#foreach ($itemDataRows as $item)
<tr>
<td class=" whitespace-nowrap ">
<x-jet-checkbox id="is_reopen" type="checkbox" class="editor_checkbox_field ml-4" title="On saving editor will be opened"
wire:model="selectedItems.{{ $item->id }}"/>
</td>
Is something wrong with definition of $itemDataRows ? Why $itemDataRows is empty in selectAllItems method, but on my template all items are visible ok....
Thanks in advance!
In Livewire you can pass the data via the class variables. And in the mount function you can fill the variable. For Example.
Important Note: The Class Vars must be public!
public $selectedItems = [];
public function mount(): void
{
$this->selectedItems = ['data' => 'Hello World'];
}
public function render()
{
return view('livewire.admin.items.crud-items')->layout('layouts.admin');
}
Update
This must have something to do with the Livewire Lifecyle. Every Livewire component goes through a lifecycle. Lifecycle hooks allow you to run code at any stage of the component's lifecycle or before updating certain properties. In your case, use the mount hook.
You initialise the variable itemDataRows in the render function. A request then calls the method selectAllItems. There you have to initialise itemDataRows again, because the state is no longer there during render or mount.
Solution: create a method getItemDataRows()
private getItemDataRows()
{
$this->itemDataRows => Item::orderBy('created_at', 'desc')
...
->paginate($backend_per_page);
}
then you can call those in the render method and in the selectAllItems method too.
public function selectAllItems()
{
$this->selectedItems= [];
$this->itemDataRows => $this->getItemDataRows();
...
// your code
}

BadMethodCallException Method username does not exist

I have a very weird problem. When I'm submitting the form, it throws an error with server-side validation.
BadMethodCallException
Method username does not exist.
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $username = 'username';
protected $redirectTo = '/dashboard';
protected $guard = 'web';
public function getLogin()
{
if (Auth::guard('web')->check())
{
return redirect()->intended('dashboard');
}
return view('login');
}
public function postLogin(Request $request)
{
$auth = Auth::guard('web')->attempt(['username' => $request->username(),
'password' => $request->password(), 'active' => 1]);
if ($auth)
{
return redirect()->route('dashboard');
}
return redirect()->route('/');
}
public function getLogout()
{
Auth::guard('web')->logout();
return redirect()->route('/');
}
}
The problem is that you trying to access the input property as method, I mean $request->username() and same with password, there is no username method, that's why you are getting error. you can access input with input() method like $request->input('username') or via dynamic property $request->username
read more at docs

Mockery mock not returning specified value

I'm using Mockery in my Laravel project to mock the User Eloquent model and test a route.
This is how I test the /api/user/activate route:
<?php
use Illuminate\Support\Facades\Session;
class ActivateTest extends TestCase
{
private $userMock;
public function setUp()
{
parent::setUp();
$this->userMock = Mockery::mock('App\User');
Session::start();
}
public function tearDown()
{
Mockery::close();
}
public function testActivate()
{
$this->userMock->shouldReceive('where->first')->once()->andReturn('test');
$this->userMock->shouldReceive('activate')->once();
$response = $this->call('POST', '/api/user/activate', [
'activationToken' => '838jfjnvu83u3',
'_token' => csrf_token()
]);
// This will be displayed in the PHPunit output
print_r($response->getContent());
$this->assertResponseStatus(200);
}
}
The problem I'm having is that the andReturn('test') doesn't seem to work. The PHPunit result is:
F{"error":{"message":null,"statusCode":404}}
Time: 276 ms, Memory: 15.50Mb
There was 1 failure:
1) ActivateTest::testActivate
Failed asserting that 404 matches expected 200.
This is the content of the activate() in the UserController:
public function activate(Request $request)
{
$activation = $request->input();
$user = $this->user->where('activationToken', $activation['activationToken'])->first();
if(!$user) return $this->respondNotFound($user);
try
{
$user->activate($activation['password']);
}
catch(ModelException $e)
{
return $this->respondInternalError($e->errorMessages());
};
return $this->respondCreated('Account activated.');
}
The problem is that $user in the controller is null because the mock is not returning test (in that case the condition would evaluate to true and I wouldn't get a 404 response).
Edit:
I also tried using PHPunit mocking but it wasn't successful:
$this->userMock = $this->getMockBuilder('App\User')->setMethods(['where', 'first', 'activate'])->getMock();
$this->userMock->expects($this->once())->method('where')->willReturn($this->userMock);
$this->userMock->expects($this->once())->method('first')->willReturn('test');
$this->userMock->expects($this->once())->method('activate');
It's not enough to mock an object. You need to get that mocked object to be injected into the class which contains that activate() function.
You can do that in your setUp() function as well. Try adding this...
$this->app->instance('App/User', $this->userMock);
That will tell Laravel when you want to inject an instance of App/User, to inject the mock object you just created instead.
The issue was caused by ->first() since it's not a method existing neither on the Eloquent or User classes.
To solve it I created a new UserRepository and injected it as a dependency in the controller constructor.
class UserRepository implements UserRepositoryInterface
{
/**
* #var User
*/
protected $user;
/**
* #param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* #param $activationToken
* #return mixed
*/
public function whereActivationToken($activationToken)
{
return $this->user->where('activationToken', $activationToken)->first();
}
}
Injection in the UserController:
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
And this is how the test PostActivateTest class looks like now:
use Illuminate\Support\Facades\Session;
class PostActivateTest extends TestCase
{
private $user;
private $userRepositoryMock;
public function setUp()
{
parent::setUp();
$this->user = Mockery::mock('App\User');
$this->userRepositoryMock = Mockery::mock('Repository\Database\UserRepository');
$this->app->instance('App\User', $this->user);
$this->app->instance('Bloom\BloomCRM\Repository\Database\UserRepository', $this->userRepositoryMock);
Session::start();
}
public function tearDown()
{
Mockery::close();
}
public function testActivate()
{
$this->userRepositoryMock->shouldReceive('whereActivationToken')->once()->andReturn($this->user);
$this->user->shouldReceive('activate')->once();
$this->call('POST', '/api/user/activate', [
'activationToken' => '838jfjnvu83u3',
'password' => 'test',
'_token' => csrf_token()
]);
$this->assertResponseStatus(201);
}
}

Unit testing helpers in CakePHP

I created a new helper called AdvHtmlHelper.
class AdvHtmlHelper extends AppHelper {
var $helpers = array('Form');
function textbox($fieldName, $options = array()) {
$output = $this->Form->input($fieldName, array('before' => '<div class="outerdiv"><div class="leftfields"><div class="txt1">', 'between' => '</div><div class="colon"> : </div></div><div class="rightfields"><div class="input">'));
$output .= '</div></div></div><div class="space"></div>';
return $output;
}
}
And I created a test for it
App::import('Helper', 'AdvHtml');
App::import('Helper', 'Form');
App::import('Helper', 'Html');
App::import('Core', 'View');
class AdvHtmlTest extends CakeTestCase {
private $advHtml = null;
//Here we instantiate our helper, and all other helpers we need.
public function startTest() {
$this->advHtml = new AdvHtmlHelper();
$this->advHtml->Form = new FormHelper();
$this->advHtml->Form->Html = new HtmlHelper();
$this->view = new View($this->Controller);
}
//testing textbox() function.
public function testTextbox() {
$result = '<div class="input text"><div class="outerdiv"><div class="leftfields"><div class="txt1"><label for="new">New</label></div><div class="colon"> : </div></div><div class="rightfields"><div class="input"><input name="data[new]" type="text" id="new" /></div></div></div></div><div class="space"></div>';
$this->assertEqual($result, $this->advHtml->textbox('new'));
}
}
I get the following error when I try to run the test. Line 10 of the helper code is the call to the form helper.
Fatal error: Call to a member function input() on a non-object in /opt/lampp/htdocs/mali/app/views/helpers/adv_html.php
How do I test a helper which calls another helper?
on line 10
EDIT: Answered. Updated with my final test case for reference.
You have to set the form helper as a property of the advHtml helper when setting up the helpers:
public function startTest() {
$this->advHtml = new AdvHtmlHelper();
$this->advHtml->Form = new FormHelper();
}

Zend Framework: How to unit test a model using Zend_Service_Twitter

I have been getting into Unit Testing with Zend Framework. I am getting used to the other things it provide but I am having a hard time understanding Mock Objects.
For this example, I am trying to use a Mock Object to test out my model.
<?php
class Twitter_Model_Twitter
{
private $_twitter;
/**
* Make the options injectable.
* __contruct($auth, $key)
*/
public function __construct()
{
$config = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV);
$key = $config->encryption->salt;
$iv_size = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$password = mcrypt_decrypt(MCRYPT_XTEA, $key, $password, MCRYPT_MODE_ECB, $iv);
$this->_twitter = new Zend_Service_Twitter($username, $password);
}
public function verifyCredentials()
{
return $this->_twitter->account->verifyCredentials();
}
public function friendsTimeline($params)
{
return $this->_twitter->status->friendsTimeline($params);
}
}
For my unit test:
require_once ('../application/models/Twitter.php');
class Model_TwitterTest extends ControllerTestCase
{
/**
* #var Model_Twitter
*/
protected $_twitter;
public function testfriendsTimeline()
{
$mockPosts = array('foo', 'bar');
//my understanding below is:
//get a mock of Zend_Service_Twitter with the friendsTimeline method
$twitterMock = $this->getMock('Zend_Service_Twitter', array('friendsTimeline'));
/*
line above will spit out an error:
1) testfriendsTimeline(Model_TwitterTest)
Missing argument 1 for Mock_Zend_Service_Twitter_9fe2aeaa::__construct(), called in
/Applications/MAMP/bin/php5/lib/php/PHPUnit/Framework/TestCase.php on line 672 and
defined /htdocs/twitter/tests/application/models/TwitterTest.php:38
*/
$twitterMock->expects($this->once())
->method('friendsTimeline')
->will($this->returnValue($mockPosts));
$model = new Twitter_Model_Twitter();
$model->setOption('twitter', $twitterMock);
$posts = $model->friendsTimeline(array('count'=>20));
$this->assertEquals($posts, $mockPosts);
}
}
How would you test the following?
1) verifyCredentials()
2) friendsTimeline()
Thanks,
Wenbert
I am going to answer this question. I think I have made this work thanks to zomg from #zftalk.
Here is my new Twitter Model:
<?php
//application/models/Twitter.php
class Twitter_Model_Twitter
{
private $_twitter;
private $_username;
private $_password;
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
$this->_twitter = new Zend_Service_Twitter($this->_username, $this->_password);
} else {
$twitterAuth = new Zend_Session_Namespace('Twitter_Auth');
$config = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV);
$key = $config->encryption->salt;
$iv_size = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$password = mcrypt_decrypt(MCRYPT_XTEA, $key, $twitterAuth->password, MCRYPT_MODE_ECB, $iv);
$username = $twitterAuth->username;
$this->_twitter = new Zend_Service_Twitter($username, $password);
}
}
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$pieces = explode('_', $key);
foreach($pieces AS $piece_key => $piece_value) {
$pieces[$piece_key] = ucfirst($piece_value);
}
$name = implode('',$pieces);
$method = 'set' . $name;
//$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
//I added this method. So that I could "inject"/set the $_twitter obj
public function setTwitter($obj)
{
$this->_twitter = $obj;
return $this;
}
public function verifyCredentials()
{
return $this->_twitter->account->verifyCredentials();
}
public function friendsTimeline($params)
{
return $this->_twitter->status->friendsTimeline($params);
}
//in the real code, more will go here...
}
And in my Unit Test, I have this:
<?php
// tests/application/models/TwitterTest.php
require_once ('../application/models/Twitter.php');
class Model_TwitterTest extends ControllerTestCase
{
public function testVerifyCredentials()
{
$stub = $this->getMock('Zend_Service_Twitter', array('verifyCredentials'),array(),'',FALSE);
//FALSE is actually the 5th parameter to flag getMock not to call the main class. See Docs for this.
//Now that I have set the $_twitter variable to use the mock, it will not call the main class - Zend_Rest_Client (i think)
$stub->expects($this->once())
->method('verifyCredentials');
$model = new Twitter_Model_Twitter();
//this is the part where i set the $_twitter variable in my model to use the $stub
$model->setOptions(array('twitter'=>$stub));
$model->verifyCredentials();
}
}
Anyways, I think I got it working.
1) The unit test no longer tried to connect to twitter.com:80
2) After I got the setOptions() working in the Twitter_Model, $model->verifyCredentials() in my unit test was successfully called.
I will wait for others in Stackoverflow to confirm that is the right answer. For the meantime, would like to hear from you guys.
Thanks!!!