Symfony2,Doctrine,MongoDb: Unable to pass query result to view - mongodb

I'm trying to work my way through the DoctrineMongoDBBundle tutorial in the Symfony documentation book. I have created the test "Products" collection, and am able to insert into it without any problems, however, I can't seem to be able to read back out of it. Or at least, I can't get any result to print in my view.
The SymfonyProfiler is showing that the query is being executed. However, nothing shows up on my screen. And if I don't comment out my foreach loop in the view, then the toolbar doesn't even show up.
Controller Code:
/**
* #route("/dbReadTest2/{id}")
* #Template()
*/
public function showAction()
{
$repository = $this->get('doctrine_mongodb')
->getManager()
->getRepository('AcmeStoreBundle:Product');
$products = $repository->findAll();
return $this->render('AcmeStoreBundle:Default:index.html.twig', array(
'products' => $products,
));
}
View Code:
{% if products.count %}
In if Block
<ul>
{% for product in products %}
In For Loop
<li>{{ product.name }} </li>
{% endfor %}
</ul>
{% else %}
<p>. There are no products yet!</p>
{% endif %}
<p>End of Page</p>
The only output that I get after loading is "In If Block". None of the other comments show up.
Thank you!

I work with doctrine this way:
Controller:
$products = $this->getDoctrine()->getRepository("AcmeStoreBundle:Product")->findProducts();
ProductRepository:
class GalleryRepository extends EntityRepository
{
public function findProducts(){
return $this->getEntityManager()->getRepository("TLWEntitiesBundle:Product")->findAll();
}
}
The rest of the code seems ok.

#user2566987
1) definitely your codes in twig to access the passing $products variable is wrong. I don't have enough time to code it for you but i can guide you to the solution
2) replace all code in your view with {{ dump(products) }} then reload the page you will see all your data from there decide whether they are php data object or php data array and output them with the appropriate syntax.
3) You can not access a property field of a class if they are not existing for example is count a private member of class products. I do not see it there in the link tutorial http://symfony.com/doc/master/bundles/DoctrineMongoDBBundle/index.html
i hope it helps.

Related

Laravel - Form - Eager Loading

Hi I'm working on a CRUD interface to update an Event.
Event Class Model a Event belongs to many Organizer
class Event extends Model
{
public function organizers()
{
return $this->belongsToMany('App\Organizer')->withTimestamps();
}
}
Organizer Class Model
class Organizer extends Model
{
public function event()
{
return $this->belongsToMany('App\Event')->withTimestamps();
}
}
Event controller I send the event along with its organizer(s)
public function edit($id)
{
$event = Event::with(['organizers'])->findOrFail($id);
return view('event.edit', ['event' => $event]);
}
event.edit view
<? $organizers = DB::table('organizers')->lists('name', 'id'); ?>
{!! Form::model($event, [
'method' => 'PATCH',
'route' => ['eventUpdate', $event->id]
]) !!}
<div id="organizer_id-group" class="form-group">
{{Form::select('organizer[id]', $organizers, null,['class'=>'form-control'])}}
</div>
{!! Form::submit('Update', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
For the moment I see the whole list of organizers which is good but the default value, or the selected one does not correspond to the one linked to the event.
I tried
<div id="organizer_id-group" class="form-group">
#foreach($event->organizers as $key =>$organizer)
{{Form::select($organizer->id, $organizers, null,['class'=>'form-control'])}}
#endforeach
</div>
but the selected organizer is always the first even if $organizer->id = 2
Any help would be welcome. Thank you
When working with Form::model it could be easier to work with the name of the property in the database.
If you have
{{Form::select('organizer[id]', $organizers, null,['class'=>'form-control'])}}
Then the value of the form will be set to $event->organizer->id if it exists.
Also from your use case it looks like an organizer belongsTo an event. You do not show the organizer model, but inside the event model you define a many-to-many relationship. This implies that you have $event->organizers.
From the information available you might want to change the relationship to
return $this->belongsTo('App\Organizer');
Amendment / Update (after additional information)
It is clear now, you are indeed working with a many-to-many collection. I have not managed to get Form model binding to work with belongsToMany collections so you need to set the 3rd parameter to physically define the organizers that need to be selected.
The most elegant way I know about is to define an accessor on your Event object for the event organizer ids. This way you can use a call to old for a validation error and then define a default as well which are the values for the initial edit.
Notice that in the form we are using an array for the multiple values.
{{Form::select('organizers[]', $organizers, old('organizers', $event->organizers_ids), ['class' => 'form-control', 'multiple'])}}
Then inside your Model you add the accessor to return the ids.
public function getOrganizersIdsAttribute()
{
return $this->organizers->lists('id')->all();
}
Answer# Update after Leon suggestions
This is what I needed to do to make it works
<div id="organizer_id-group" class="form-group">
{{Form::select('organizers[]', $organizers, $event->organizers->lists('id')->all(),['class'=>'form-control', 'multiple'])}}
<span class="help-block"></span>
</div>
To make a selection by default you have to change the third argument (https://laravelcollective.com/docs/5.0/html#opening-a-form)

how to display mongodb date in symfony2 twig template?

I have a website listing products using symfony2 and mongodb
I added the items into mongodb with create date and need to display all items in my twig template.
For this
In my controller I passed the array itemlist to twig template.
My twig template
{% for item in itemlist %}
<h4>{{item.name}}</h4>
<p>{{item.name}}</p>
{{item.createdate}}
{% endfor %}
Here I am not getting the item.createdate
How to directly display the mongo date in twig template?
is there any twig extension for this?
{{ sampleDate.sec|date('Y-m-d') }}
This is an extension class that might work for you:
class MongoDateExtension extends \Twig_Extension
{
/**
* #inheritdoc
*/
public function getName()
{
return 'mongoDate_extension';
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('convertMongoDate', array($this, 'convertMongoDateFilter')),
);
}
public function convertMongoDateFilter(\MongoDate $mongoDate)
{
return new \DateTime('#' . $mongoDate->sec);
}
}
Then register the class to your dependency injection container by adding the following snippet to your services.xml. Consider that you have to adjust the class path in the example.
<service id="twig.extension.mongo_date"
class="Path\To\Your\Bundle\Twig\Extension\MongoDateExtension">
<tag name="twig.extension"/>
</service>
The extension will convert the mongo date to a php \DateTime object. It will perform the transformation with an accuracy of seconds so if you need microseconds as well you will need to adjust the extension.
Finally in your twig template you can just use the twig date formatting extension:
{{ sampleDate|convertMongoDate|date('Y-m-d') }}
which will print 2013-11-05
I've experienced problems with timezones using sec.
Instead I used toDateTime which works fine.
{{ sampleDate.toDateTime|date('Y-m-d') }}
MongoDate::toDateTime
Th most simple way i found is :
{{ event.begin.toDateTime()|date("d/m/Y H:i:s") }}

CodeIgniter - Two Forms, One Page

I'm having a fundamental problem in understanding the concept of MVC and displaying more than one form at a time. I've tried a variety of methods but I'm still stuck - and that's because I don't think I'm understanding CI and MVC correctly.
I tried using 2 different views for the two different forms. Didn't work. I tried using one function per form in my controller. That didn't work either. I don't know what to do.
Should I be doing this;
Create a controller and have an index() function in it.
Build up my form elements for each form within this index()
Create 1 view that displays both forms and call it from within index()
Use form_open to direct the submit action to another function - call it validate()
Validate everything that comes in, send back errors
Somehow, and this is the main bit I don't get, complete an action if the form has been filled in correctly.
6 Is my biggest problem. Don't know how to do that. For example, on successful completion of the form I want my user to have created a directory at a chosen location - so I'm using mkdir() - so do I need an if statement within the validate() function or what??
UPDATE
Here is the code I have created so far;
Controller:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// Forms CodeIgniter controller
class Admin extends CI_Controller {
// Controller constructor
public function __construct()
{
parent::__construct();
// Load form helper required to validate forms
$this->load->helper(array('form', 'url'));
$this->load->library('form_validation');
}
//*************************************************//
// Prepare data for the view to output the forms
public function index()
{
//*****************************************************//
//returns a drop down list of radio buttons, one for each directory
$map_one = $this->recursive_model->iterate_add_folder_names();
$data['folder_list_add'] = $map_one;
//****************************************************//
//*****************************************************//
//also returns a drop down list of radio buttons (slightly different), one for each directory
$map_two = $this->recursive_model->iterate_folder_names();
$data['folder_list_select'] = $map_two;
//****************************************************//
//load the views and the forms
$this->load->view('templates/header.php');
$this->load->view('admin/add_new_folder.php', $data);
$this->load->view('admin/add_new_file.php', $data);
$this->load->view('templates/small_footer.php');
}
//*************************************************//
//function if adding a new directory to the current structure
public function add_folder()
{
//need to select a directory for it to go under
$this->form_validation->set_rules('new_folder', 'New Folder', 'required');
//and name the new directory
$this->form_validation->set_rules('new_folder_name', 'New Folder Name', 'required');
if ($this->form_validation->run() === FALSE)
{
$this->index();
}
else
{
if($this->input->post())
{
$new_folder = $this->input->post('new_folder');
$new_folder_name = $this->input->post('new_folder_name');
$folder_path = "/var/www/html/mike/content".$new_folder."/".$new_folder_name;
mkdir($folder_path, 0777);
$this->index();
}
}
}
//*************************************************//
public function add_file()
{
//folder location and name of file
$folder_name = $this->input->post('folder_name');
$new_folder_name = $this->input->post('file_name');
//validation rules
$this->form_validation->set_rules('folder_name', 'Folder Name', 'required');
$this->form_validation->set_rules('file_name', 'File Name', 'required');
//if there is an error with validation
if ($this->form_validation->run() === FALSE)
{
//gets stuck here every time when trying to upload a new folder :(
$this->index();
}
//if there is not an error with validation
else
{
//$folder_name will be something like "http://www.example.com/publications/people/reports"
$config['upload_path'] = $folder_name;
$config['allowed_types'] = 'gif|jpg|png|html|pdf|xls';
$this->load->library('upload', $config);
//if file cannot be loaded (due to $config perhaps?)
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
$this->index();
}
else
{
$data = array('upload_data' => $this->upload->data());
$this->index();
}
}
}
//*************************************************//
}
Here is one view (add_new_file.php);
<div id="container">
<h1>Upload A File/Publication</h1>
<div id="body">
<?php //echo $error;?>
<?php echo form_open_multipart('admin/add_file');?>
<?php echo $folder_list_select; ?>
<input type="file" name="file_name" size="20" />
<input type="submit" value="upload" />
</form>
</div>
Here is the other (add_new_folder.php)
div id="container">
<h1>Add A New Folder</h1>
<div id="body">
<?php echo validation_errors(); ?>
<?php echo form_open('admin/add_folder');?>
<?php echo $folder_list_add; ?>
New Folder Name: <input type="text" name="new_folder_name">
<input type="submit" value="upload" />
</form>
</div>
I hope this helps answer this thread.
Basically, I can get the first section to work - adding a folder - but I cannot get the adding a file to work. This is because if ($this->form_validation->run() === FALSE) is always returning false. I think it might be looking at the form elements in the other form - which it shouldn't do. What am I missing?
Should I be doing this;
1 . Create a controller and have an index() function in it.
[let's, for the sake of conversation, call this controller Users thx -ed]
Sure. That's cool. You could also have a function in that Controller called edit, or banana or whatever; either way works. With using just the index method (function), the url might look like http://example.com/index.php/users whereas if you add another method to the controller like banana, the url might look like http://example.com/index.php/users/banana.
2 . Build up my form elements for each form within this index()
Well, typically form elements are not created in the controllers. This is where the V in MVC comes in -- stuff you view goes into a view.
So, one might do something like
// Users Controller
class Users extends CI_Controller{
function index(){
//index method
}
function banana(){
$this->load->view('banana_view');
}
}
then in application/views/banana_view.php, you create your form. When you visit http://example.com/users/banana, you will see the form you created in banana_view.php.
3 . Create 1 view that displays both forms and call it from within index()
Sure, that'd work just fine. But remember that each <form></form> needs its own <input type="submit" name="Lets GO"> inside and thusly needs somewhere to send each forms data. This is the action="". You can leave it out, but beware that it will then send the form to whatever page you are currently on (in our case here, http://example.com/index.php/users/banana), so you have to have something in the banana() method to handle the form data. But, typically, it will be set via form_open(). Something like form_open('index.php/users/eat_banana'); will generate <form action="index.php/users/eat_banana"...
4 . Use form_open to direct the submit action to another function - call it validate()
Just don't call it late_for_dinner. But seriously, validate is a bit broad -- validate what? Validate why? As to validation, https://www.codeigniter.com/user_guide/libraries/form_validation.html. But you should cross that bridge after you grok the fundamentals of CodeIgniter (won't take long).
5 . Validate everything that comes in, send back errors
See last question.
6 . Somehow, and this is the main bit I don't get, complete an action if the form has been filled in correctly.
Many times people will display a success message
class Users extends CI_Controller{
function index(){
//index method
}
function banana(){
$this->load->view('banana_view');
}
// assuming form_open('index.php/users/eat_banana'); in banana_view
function eat_banana(){
//make sure that this is a POST
if($this->input->post()){
// do things with the data
// typically it gets saved to a database
// via a model (the M in MVC)
// http://ellislab.com/codeigniter/user-guide/general/models.html
if($saved_to_db){
// set message to send to the view
$data['message'] = "Everything went OK";
}else{
$data['message'] = "but who was database? data didn't save :(";
}
// load the view and send the data
$this->load->view('eat_banana', $data);
}
}
application/views/eat_banana.php:
<!DOCTYPE html>
<html>
<head></head>
<body>
<div>
<b>Form submitted.</b><br />
The message is: <?php echo $message; ?>
</div>
</html>
other times, one might instead prefer to redirect
class Users extends CI_Controller{
function index(){
//index method
}
function banana(){
$this->load->view('banana_view');
}
// assuming form_open('index.php/users/eat_banana'); in banana_view
function eat_banana(){
//make sure that this is a POST
if($this->input->post()){
// do things with the data
if($saved_to_db){
// just send them to the homepage
redirect('/');
}else{
// send them back to the form
redirect('index.php/users/banana');
}
}
}
So,
M is for model. Models are used to talk to the database.
V is for Vend view. Views render the text, forms, pictures, gifs, whatever to the screen. That's the idea anyway. There's nothing stopping you from echo'ing out an enormous unminimized javascript application from your controller. That would totally not be MVC tho.
C is for controller. Controllers call and send data to the views, receive data sent from views, take that data and send it to a model to be saved in the database (although CodeIgniter doesn't enforce this in any way either; you could if you wanted to save the data to a database directly from the controller, but this obviously defeats the MVC principal as well), retrieves data from the database and sends it to a view for display. Those are the basics anyway.

Symfony Forms: "Array to string conversion" exception in modified DateType class

I'd like to build a custom DateType class. In order to do this, I copied the class Symfony\Component\Form\Extension\Core\Type\DateType to my src/ directory and changed the class name and getName().
<?php
namespace FooBar\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
// ...
class MonthType extends AbstractType
{
// ...
public function getName()
{
return 'month';
}
// ...
}
I also registered the new type:
foobar.form.type.month:
class: FooBar\CoreBundle\Form\Type\MonthType
tags:
- { name: form.type, alias: month }
However if I try to use my new type, an exception (Array to string conversion in /var/www/foobar/app/cache/dev/twig/4d/99/945***.php) is thrown:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$default = new \DateTime('now');
$builder
->add('season', 'month', array('data' => $default))
;
}
Note: If I change 'month' to 'date' everything works flawlessly.
Does anyone know why the exception is thrown and how I can get rid of it?
How to fix
You have to define the block month_widget and use as form field template to make sf2 render the field properly.
For instance, write below in your .twig.
{% form_theme form _self %}
{% block month_widget %}
<input type="text" value="{{ value.year }}">
<input type="text" value="{{ value.month }}">
{% endblock %}
and customize the presentation as your like.
The default theme file Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig may help.
See below for more details.
http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html#creating-a-template-for-the-field
Cause of error
Symfony2 does not have the rendering block named month_widget.
MonthType that you created is child of FormType (because inherited getParent() returns 'form')
month_widget could not be found (because you haven't defined it yet), so it next tries to render form_widget.
In form_widget, there is only simple text field like <input type="text" value="{{ value }}" ..., and fails here because value is not a scalar.
The value actually isn't a DateTime but array because of DateTimeToArrayTransformer used in the class.
(as class name says, DateTime is converted into array)
So, the error is Array to string conversion.

Fetching categories and items with Zend Framework

Currently my code below works fine but it's a bit of overkill. In my controller I fetch the categories that have links and all the links in my database.
In my view I loop through all the categories and then when I want to add the links under the category I loop through all the links in my database instead I should only loop through the links that are assigned to the current category but I don't know how to do this with Zend Framework. Can anybody send me into right direction. Thank's for your time.
Controller:
public function indexAction()
{
$this->view->title = App_Translate::translate('links_title');
$this->view->headTitle($this->view->title, 'PREPEND');
$linkCat = Doctrine_Query::create()
->distinct()
->from('LinkCategory lc')
->innerJoin('lc.Link l WITH lc.id = l.link_category_id')
->orderBy('lc.id')
->execute();
$links = Doctrine_Query::create()
->from('Link')
->execute();
$this->view->linkCat = $linkCat;
$this->view->links = $links;
}
}
View:
<?php if(!empty($this->linkCat)): ?>
<ul>
<?php foreach($this->linkCat as $linkCat): ?>
<li><h2><?php echo $this->escape($linkCat['title']); ?></h2>
<?php if(!empty($this->links)): ?>
<ul>
<?php foreach($this->links as $link): ?>
<?php if($link['link_category_id'] == $linkCat['id']): ?>
<li><?php echo $this->escape($link['title']); ?></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p>No links added</p>
<?php endif; ?>
Your question is not really related to Zend Framework. You are fetching data using Doctrine, not ZF.
In your case, I think you should be able to loop over the links in the specific category using foreach($linkCat->Link as $link), seeing how you use innerJoin to load the relation.
Instead of doing those two queries, couldn't you go with only one ?
I see you have an Inner Join in your first query, so I suppose this could be possible ; I suppose you have some rule that says "One link is in one and only one category", which is a One-To-Many Relation ; maybe the Dealing with Relations part of the manual could help you.
What I would do might be :
remove the distinct from the first query, to get all links + for each one, its category
this is the list you want, isn't it ?
also, order by category and link, so it's easier to display (links being already ordered by category)
remove the second query
I suppose something like this would do :
$linkCat = Doctrine_Query::create()
->from('LinkCategory lc')
->innerJoin('lc.Link l WITH lc.id = l.link_category_id')
->orderBy('lc.name, l.name')
->execute();
(not tested, though : might need a bit more tunning)
Once you have that data, pass it to the views, where you'll do your loop -- remember lonks are already sorted by category :
display the name of the first category ; store it in a variable
loop on the links
if the category of the current link is not the same as the one of the previous link (store in the variable), then it means it's the end of a category, and the beginning of a new one
continue
when you reach the end of the links, it's also the end of the last category
This should work, I'd say (of course, you still have to code -- but the idea is here)
As a sidenote : you are using Doctrine classes, writting DQL, and all that in your Controller -- This is quite not the place : all this should go in your Model Classes, not in a Controller.
Have fun !