Is it possible to pass data from seeder to factory?
This is my PictureFactory:
class PictureFactory extends Factory{
protected $model = Picture::class;
public function definition($galleryId = null, $news = false){
if (!is_null($galleryId)){
$galley = Gallery::find($galleryId);
$path = 'public/galleries/' . $galley->name;
$newsId = null;
}
if ($news){
$path = 'public/newsPicture';
$newsId = News::all()->random(1);
}
$pictureName = Faker::word().'.jpg';
return [
'userId' => 1,
'src' =>$this->faker->image($path,400,300, 2, false) ,
'originalName' => $pictureName,
'newsId' => $newsId
];
}
}
and I use it like this in database seeder:
News::factory(3)
->has(Comment::factory()->count(2), 'comments')
->create()
->each(function($news) {
$news->pictures()->save(Picture::factory(null, true)->count(3));
});
but $galleryId and $news do not pass to PictureFactory. Where did I go wrong? And what should I do? please help me.
This is the sort of thing that factory states were made for. Assuming you are using a current (8.x) version of Laravel, define your factory like this:
<?php
namespace Database\Factories\App;
use App\Models\{Gallery, News, Picture};
use Illuminate\Database\Eloquent\Factories\Factory;
class PictureFactory extends Factory
{
protected $model = Picture::class;
public function definition()
{
return [
'userId' => 1,
'originalName' => $this->faker->word() . '.jpg',
];
}
public function withGallery($id)
{
$gallery = Gallery::findOrFail($id);
$path = 'public/galleries/' . $gallery->name;
return $this->state([
'src' => $this->faker->image($path, 400, 300, 2, false),
'newsId' => null,
]);
}
public function withNews()
{
$news = News::inRandomOrder()->first();
$path = 'public/newsPicture';
return $this->state([
'src' => $this->faker->image($path, 400, 300, 2, false),
'newsId' => $news->id,
]);
}
}
And now you can create your desired models like this:
Picture::factory()->count(3)->withNews();
// or
Picture::factory()->count(3)->withGallery($gallery_id);
I'm not certain, but I believe you should be able to do this to get your desired outcome:
Picture::factory()
->count(3)
->withNews()
->for(News::factory()->hasComments(2))
->create();
Related
I need to make request on some CRM api in my controller. For making this I have pretty big method. It's look like ugly. I know that there are some "Services" and to put additional code into Service is a good way. But I don't know what is this. Is it a custom classes into app folder? Or maybe it's Service-providers? I have read service-providers documentation and I'm not sure that service-providers is suitable for this. Here is my code:
<?php
namespace App\Http\Controllers;
use App\User;
use App\UserInfo;
use Validator;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$users = User::with('info')
->paginate(20);
$users->withPath(DIRECTORY_SEPARATOR . $request->path() .DIRECTORY_SEPARATOR);
return response()->json($users)->setEncodingOptions(JSON_UNESCAPED_UNICODE);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$data = $request->json()->all();
$rules = [
'name' => 'required',
'phone' => 'required|unique:users'
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) return response()->json(['errors'=>$validator->errors()]);
$user = new User();
$user->name = request('name');
$user->phone = request('phone');
$user_info_obj = $this->storeUserInfo();
if($user_info_obj === null){
return response('Impassible to define user geo data', 400);
}
$user->info_id = $user_info_obj->id;
$user->save();
$this->makeAMOLead($user->name,
$user->phone,
$user_info_obj->user_agent,
$user_info_obj->city,
$user_info_obj->country);
return response()->json(['success' => 'User created successfully']);
}
public function storeUserInfo()
{
$ip = request()->ip();
$reader = new \GeoIp2\Database\Reader('../resources/geo-lite2-city_20180807/GeoLite2-City.mmdb');
try {
$record = $reader->city($ip);
}
catch (\Throwable $e){
// Code bellow is for testing on localhost, Because of maybe exception instead of geo obj on localhost.
$info = new UserInfo();
$info->ip = '127.0.0.1';
$info->city = 'Some city';
$info->country = 'Some country';
$info->country_code = 'Some code';
$info->continent = 'Some continent';
$info->continent_code = 'no';
$info->user_agent = 'User agent';
$info->save();
return $info;
//return null;
}
$city = $record->city->names['ru'];
$continent = $record->continent->names['ru'];
$continent_code = $record->continent->code;
$country = $record->country->names['ru'];
$country_code = $record->country->isoCode;
$user_agent = \request()->userAgent();
$info = new UserInfo();
$info->ip = $ip;
$info->city = $city;
$info->country = $country;
$info->country_code = $country_code;
$info->continent = $continent;
$info->continent_code = $continent_code;
$info->user_agent = $user_agent;
$info->save();
return $info;
}
private function makeAMOLead($name, $phone, $userAgent, $city, $country)
{
$domain = env('AMO_DOMAIN');
$login = env('AMO_LOGIN');
$hash = env('AMO_HASH');
try {
$credentials = new \ddlzz\AmoAPI\CredentialsManager($domain, $login, $hash);
$settings = new \ddlzz\AmoAPI\SettingsStorage();
$settings->setCookiePath(env('AMO_COOKIE_FILE_PATH'));
$request = \ddlzz\AmoAPI\ClientFactory::create($credentials, $settings);
$lead = new \ddlzz\AmoAPI\Model\Amo\Lead();
$lead['name'] = $name;
if(env('AMO_PIPELINE_ID', null)){
$lead['pipeline_id'] = intval(env('AMO_PIPELINE_ID'));
}
$lead['name'] = 'New pickup user ' . $name;
$lead['custom_fields'] = [
[
'id' => env('AMO_NAME_FIELD_ID'),
'values' => [
['value' => $name],
]
],
[
'id' => env('AMO_USER_AGENT_FIELD_ID'),
'values' => [
['value' => $userAgent]
]
],
[
'id' => env('AMO_CITY_FIELD_ID'),
'values' => [
['value' => $city]
]
],
[
'id' => env('AMO_COUNTRY_FIELD_ID'),
'values' => [
['value' => $country]
]
],
];
$lead['created_at'] = time();
$result = $request->add($lead);
$pipelineId = json_decode($result)->_embedded->items{0}->id;
// create contact
$contact = new \ddlzz\AmoAPI\Model\Amo\Contact();
$contact['name'] = $name;
$contact['created_at'] = time();
$contact['leads_id'] = "$pipelineId";
// dd($request->accountInfo(), true); // Call this, if you need to know ids of default fields (like phone, or position)
$contact['custom_fields'] = [
[
'id' => env('AMO_CONTACT_PHONE_ID'),
'values' => [
[
'value' => $phone,
'enum' => 'MOB',
],
]
],
];
$result = $request->add($contact);
} catch (Exception $e) {
echo response()->json(['error' => $e->getFile() . ': ' . $e->getMessage()]);
}
}
}
Look on the makeAMOLead. This is big method in my controller and this is not ok for controller conception.
Please use repository pattern to split all the communication between the application and your data source. and call the repository functions inside your controller. It is good practice. Here is an article you can understand about that
Example:
Your functions can be separate from controller to repository.
storeUserInfo
makeAMOLeadin
Move your functions an repository and call them into your controller.
I wonder how to write proper unit test for my email sending method. It's a problem because inside method I get data from Auth object. Should I send id of user in Request?
public function sendGroupInvite(Request $request){
foreach ($request->get('data') as $item){
$invitations = new \App\Models\Invitations();
$invitations->user_id = Auth::id();
$invitations->name = $item["name"];
$invitations->email = $item["email"];
$invitations->status = 0;
$invitations->token = \UUID::getToken(20);
$invitations->username = Auth::user()->name;
$invitations->save();
$settings = UserSettings::where('user_id', Auth::id())->first();
$email = $item["email"];
$url = 'https://example.com/invite/accept/'.$invitations->token;
$urlreject = 'https://example.com/invite/reject/'.$invitations->token;
$mailproperties = ['token' => $invitations->token,
'name' => $invitations->name,
'url' => $url,
'email' => $email,
'urlreject' => $urlreject,
'userid' => Auth::id(),
'username' => Auth::user()->name,
'user_name' => $settings->name,
'user_lastname' => $settings->lastname,
'user_link' => $settings->user_link,
];
$this->dispatch(new SendMail(new Invitations($mailproperties)));
}
return json_encode(array('msg' => 'ok'));
}
I'm using Auth to get username and user id. When I testing it it's not works, because Auth it's null.
I would go with mocking the queue, something similar to this. Mock Documentation
class MailTester extends TestCase{
/**
* #test
*/
public function test_mail(){
Queue::fake();
// call your api or method
Queue::assertPushed(SendMail, function(SendMail $job) {
return $job->something = $yourProperties;
});
}
You could try "acting as" to deal with the Auth::user().
...
class MyControllerTest extends TestCase{
/**
* #test
*/
public function foo(){
$user = App\Users::find(env('TEST_USER_ID')); //from phpunit.xml
$route = route('foo-route');
$post = ['foo' => 'bar'];
$this->actingAs($user)//a second param is opitonal here for api
->post($route, $post)
->assertStatus(200);
}
}
I'm using two models in one controller. one of them is database model (model) another model is for sending sms (smsModel).
I have problem in smsModel.
I got this error in my result:
Class 'fcadmin\models\SoapClient' not found
How can I fix it?
My controller:
public function actionCreate($id) {
$model = new Requestresult();
$smsModel = new SmsSender();
$request_model = Request::findOne(['id' => $id]);
$model->CodeKargah = $request_model->CodeKargah;
$model->month = $request_model->month;
$model->trackingCode = $request_model->trackingCode;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
$smsModel->sendSms('09193452126', 'sdf');
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
smsModel:
public function sendSms($to, $text) {
$options = [
'login' => 'myusername',
'password' => 'mypassword'
];
$client = new SoapClient('http://sms.hostiran.net/webservice/?WSDL', $options);
$messageId = $client->send($to, $text);
sleep(3);
return ($client->deliveryStatus($messageId));
}
You need to read up about namespaces. If you're in a namespace and don't tell PHP that you want to use the global namespace, it will look for classes of name x in the current namespace.
In your case you need to be using new \SoapClient.
I was following this book to install the ZendSearh on the application, I did exactly as it's written and I'm getting a Fatal error: Class 'ZendSearch\Lucene\Lucene' not found in /var/www/CommunicationApp/module/Users/src/Users/Controller/SearchController.php on line 107
<?php
namespace Users\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Http\Headers;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Adapter\DbTable as DbTableAuthAdapter;
use Users\Form\RegisterForm;
use Users\Form\RegisterFilter;
use Users\Model\User;
use Users\Model\UserTable;
use Users\Model\Upload;
use Users\Model\ImageUpload;
use Users\Model\ImageUploadTable;
use ZendSearch\Lucene;
use ZendSearch\Lucene\Document;
use ZendSearch\Lucene\Index;
class SearchController extends AbstractActionController
{
protected $storage;
protected $authservice;
public function getAuthService()
{
if (! $this->authservice) {
$this->authservice = $this->getServiceLocator()->get('AuthService');
}
return $this->authservice;
}
public function getIndexLocation()
{
// Fetch Configuration from Module Config
$config = $this->getServiceLocator()->get('config');
if ($config instanceof Traversable) {
$config = ArrayUtils::iteratorToArray($config);
}
if (!empty($config['module_config']['search_index'])) {
return $config['module_config']['search_index'];
} else {
return FALSE;
}
}
public function getFileUploadLocation()
{
// Fetch Configuration from Module Config
$config = $this->getServiceLocator()->get('config');
if ($config instanceof Traversable) {
$config = ArrayUtils::iteratorToArray($config);
}
if (!empty($config['module_config']['upload_location'])) {
return $config['module_config']['upload_location'];
} else {
return FALSE;
}
}
public function indexAction()
{
$request = $this->getRequest();
if ($request->isPost()) {
$queryText = $request->getPost()->get('query');
$searchIndexLocation = $this->getIndexLocation();
$index = Lucene\Lucene::open($searchIndexLocation);
$searchResults = $index->find($queryText);
}
// prepare search form
$form = new \Zend\Form\Form();
$form->add(array(
'name' => 'query',
'attributes' => array(
'type' => 'text',
'id' => 'queryText',
'required' => 'required'
),
'options' => array(
'label' => 'Search String',
),
));
$form->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Search',
'style' => "margin-bottom: 8px; height: 27px;"
),
));
$viewModel = new ViewModel(array('form' => $form, 'searchResults' => $searchResults));
return $viewModel;
}
public function generateIndexAction()
{
$searchIndexLocation = $this->getIndexLocation();
$index = Lucene\Lucene::create($searchIndexLocation); // line 107
$userTable = $this->getServiceLocator()->get('UserTable');
$uploadTable = $this->getServiceLocator()->get('UploadTable');
$allUploads = $uploadTable->fetchAll();
foreach($allUploads as $fileUpload) {
//
$uploadOwner = $userTable->getUser($fileUpload->user_id);
// id field
$fileUploadId= Document\Field::unIndexed('upload_id', $fileUpload->id);
// label field
$label = Document\Field::Text('label', $fileUpload->label);
// owner field
$owner = Document\Field::Text('owner', $uploadOwner->name);
if (substr_compare($fileUpload->filename, ".xlsx", strlen($fileUpload->filename)-strlen(".xlsx"), strlen(".xlsx")) === 0) {
// index excel sheet
$uploadPath = $this->getFileUploadLocation();
$indexDoc = Lucene\Document\Xlsx::loadXlsxFile($uploadPath ."/" . $fileUpload->filename);
} else if (substr_compare($fileUpload->filename, ".docx", strlen($fileUpload->filename)-strlen(".docx"), strlen(".docx")) === 0) {
// index word doc
$uploadPath = $this->getFileUploadLocation();
$indexDoc = Lucene\Document\Docx::loadDocxFile($uploadPath ."/" . $fileUpload->filename);
} else {
$indexDoc = new Lucene\Document();
}
$indexDoc->addField($label);
$indexDoc->addField($owner);
$indexDoc->addField($fileUploadId);
$index->addDocument($indexDoc);
}
$index->commit();
}
}
It has its own repository on github.
https://github.com/zendframework/ZendSearch
You have to load it via composer or just download and put it under vendor folder.
I am looking for a way to access and change the DATABASE_CONFIG variables, based on user input. Using CakePHP I created a custom datasource, based on the one provided in the docs, to access an external API. The API returns a JSON string containing the 12 most recent objects. I need to be able to change the page number in the API request to get the next 12 results, as well as accept a free text query entered by the user.
app/Config/Database.php
class DATABASE_CONFIG {
public $behance = array(
'datasource' => 'BehanceDatasource',
'api_key' => '123456789',
'page' => '1',
'text_query' => 'foo'
);
}
app/Model/Datasource/BehanceDataSource.php
App::uses('HttpSocket', 'Network/Http');
class BehanceDatasource extends DataSource {
public $description = 'Beehance datasource';
public $config = array(
'api_key' => '',
'page' => '',
'text_query' => ''
);
public function __construct($config) {
parent::__construct($config);
$this->Http = new HttpSocket();
}
public function listSources($data = null) {
return null;
}
public function describe($model) {
return $this->_schema;
}
public function calculate(Model $model, $func, $params = array()) {
return 'COUNT';
}
public function read(Model $model, $queryData = array(), $recursive = null) {
if ($queryData['fields'] === 'COUNT') {
return array(array(array('count' => 1)));
}
$queryData['conditions']['api_key'] = $this->config['api_key'];
$queryData['conditions']['page'] = $this->config['page'];
$queryData['conditions']['page'] = $this->config['text_query'];
$json = $this->Http->get('http://www.behance.net/v2/projects', $queryData['conditions']);
$res = json_decode($json, true);
if (is_null($res)) {
$error = json_last_error();
throw new CakeException($error);
}
return array($model->alias => $res);
}
}
Is there anyway to access and change the $behance array, or is there another way to go about accessing an external API with cakePHP that I am totally missing?